Compare commits
16 Commits
2025.07.20
...
2025.10.09
Author | SHA1 | Date | |
---|---|---|---|
|
c6942f366f | ||
|
9473f9387f | ||
|
0b10a38921 | ||
|
6e8c00c313 | ||
|
cf8e3ac9ae | ||
|
727906d461 | ||
|
756312021a | ||
|
d6d3cd6d00 | ||
|
6d43975f0b | ||
|
013a41bb02 | ||
|
3daee8bc4d | ||
|
342c025697 | ||
|
a603f5160a | ||
|
846f09f817 | ||
|
d930d6774d | ||
|
513af6bfa7 |
@@ -116,4 +116,6 @@ binary_sensor:
|
||||
pin:
|
||||
number: D5
|
||||
mode: INPUT
|
||||
```
|
||||
```
|
||||
|
||||
<BlogDiscussions/>
|
@@ -49,4 +49,6 @@ Now we need to create a handler of the Aqara button in `automations.yaml`:
|
||||
- data:
|
||||
message: "Someone near your front door"
|
||||
service: notify.push
|
||||
```
|
||||
```
|
||||
|
||||
<BlogDiscussions/>
|
@@ -134,4 +134,6 @@ Now we can rely on that group’s state to check if there is nobody home. I’ve
|
||||
service: notify.push
|
||||
```
|
||||
|
||||
That is all for now. Thanks for reading.
|
||||
That is all for now. Thanks for reading.
|
||||
|
||||
<BlogDiscussions/>
|
@@ -208,4 +208,6 @@ Also, this automation should be able to run in parallel to make it possible to h
|
||||
```yaml
|
||||
mode: parallel
|
||||
max: 10
|
||||
```
|
||||
```
|
||||
|
||||
<BlogDiscussions/>
|
@@ -40,4 +40,6 @@ binary_sensor:
|
||||
friendly_name: "Bed occupancy"
|
||||
device_class: occupancy
|
||||
value_template: "{{is_state('binary_sensor.bed_occupancy_door_side', 'off') or is_state('binary_sensor.bed_occupancy_window_side', 'off')}}"
|
||||
```
|
||||
```
|
||||
|
||||
<BlogDiscussions/>
|
@@ -42,4 +42,6 @@ action:
|
||||
|
||||

|
||||
|
||||
Here we are checking for the `engine is off` text in incoming SMS to perform some action. For example, to set the value of some `input_boolean`.
|
||||
Here we are checking for the `engine is off` text in incoming SMS to perform some action. For example, to set the value of some `input_boolean`.
|
||||
|
||||
<BlogDiscussions/>
|
@@ -105,3 +105,5 @@ switch:
|
||||
```
|
||||
|
||||
That’s it for today. Thanks for reading.
|
||||
|
||||
<BlogDiscussions/>
|
@@ -237,4 +237,6 @@ Before placing our power supply inside the TV we need to make sure it is isolate
|
||||
|
||||
So the overall picture is looking like this:
|
||||
|
||||

|
||||

|
||||
|
||||
<BlogDiscussions/>
|
@@ -85,4 +85,6 @@ Compile, flash, open logs and start firing into our device with your IR remote.
|
||||
[17:34:51][D][remote.samsung:055]: Received Samsung: data=0xE0E020DF
|
||||
```
|
||||
|
||||
You can now use this data with the ESPHome [Remote Transmitter](https://esphome.io/components/remote_transmitter.html) component to build, for example, something like [this](/2021-01-13-building-wifi-ir-remote-control-for-any-tv-with-esp8266-wemos-d1-mini-and-esphome/index.md).
|
||||
You can now use this data with the ESPHome [Remote Transmitter](https://esphome.io/components/remote_transmitter.html) component to build, for example, something like [this](/2021-01-13-building-wifi-ir-remote-control-for-any-tv-with-esp8266-wemos-d1-mini-and-esphome/index.md).
|
||||
|
||||
<BlogDiscussions/>
|
@@ -120,4 +120,6 @@ Decreasing the volume action is the same, but with negative `target`:
|
||||
service: esphome.amplifier_set_volume
|
||||
data:
|
||||
target: -50
|
||||
```
|
||||
```
|
||||
|
||||
<BlogDiscussions/>
|
@@ -119,4 +119,6 @@ You'll get an email notification on the address added here when there will be ti
|
||||
|
||||
After certificate generation, we should open Settings on the same Certificates tab, find our domain service, and choose the newly generated certificate for it.
|
||||
|
||||
That's it!
|
||||
That's it!
|
||||
|
||||
<BlogDiscussions/>
|
@@ -43,4 +43,6 @@ In your Home Assistant go to _Settings -> Devices & services_ and click the _"Ad
|
||||
|
||||
In the configuration window that appeared you need to set your NAS's local IP address as a _Host_ and leave the _Port_ number unchanged.
|
||||
|
||||
Hit _Submit_ and you are done.
|
||||
Hit _Submit_ and you are done.
|
||||
|
||||
<BlogDiscussions/>
|
@@ -54,4 +54,6 @@ The authorization header should have the next format: `PVEAPIToken=<proxmox user
|
||||
|
||||
You can use `shutdown` or `reboot` as a `command`.
|
||||
|
||||
After restarting Home Assistant you will have a new service available. In our example, it would be `rest_command.spacedock_one_shutdown`. Calling it will do the trick.
|
||||
After restarting Home Assistant you will have a new service available. In our example, it would be `rest_command.spacedock_one_shutdown`. Calling it will do the trick.
|
||||
|
||||
<BlogDiscussions/>
|
@@ -105,4 +105,6 @@ And up it back with a system service, also enabling it:
|
||||
sudo systemctl start wg-quick@wg0 && sudo systemctl enable wg-quick@wg0
|
||||
```
|
||||
|
||||
Further interface control should be performed through `systemctl` as well like `systemctrl stop`, `systemctl restart` or `systemctl status`.
|
||||
Further interface control should be performed through `systemctl` as well like `systemctrl stop`, `systemctl restart` or `systemctl status`.
|
||||
|
||||
<BlogDiscussions/>
|
@@ -98,4 +98,6 @@ Cron job line to add:
|
||||
|
||||
```
|
||||
@reboot sleep 30s && /usr/sbin/etherwake -i vmbr0 <mac_address>
|
||||
```
|
||||
```
|
||||
|
||||
<BlogDiscussions/>
|
187
blog/2025-10-07-proxmox-ve-on-hetzner-dedicated-server/index.md
Normal file
187
blog/2025-10-07-proxmox-ve-on-hetzner-dedicated-server/index.md
Normal file
@@ -0,0 +1,187 @@
|
||||
---
|
||||
slug: proxmox-ve-on-hetzner-dedicated-server
|
||||
title: "Proxmox VE and PBS on Hetzner dedicated server"
|
||||
tags: [self-hosting, homelab, Proxmox, Proxmox VE, PBS, Hetzner, Tailscale, Caddy]
|
||||
image: /img/homelab.png
|
||||
---
|
||||
|
||||
Circumstances forced me to move all my self-hosted services to a location that does not depend on my living place. Someday, I hope I'll have all that hosted at home again, but now I want to describe my path of setting up a Proxmox VE on a Hetzner dedicated server together with Proxmox Backup Server and Tailscale with backups on Backblaze. This could look like a trivial task at first, but I faced some issues and obstacles I want to document here, for me to remember and for others to be informed.
|
||||
|
||||
I want to warn that this is not a full step-by-step guide. It is rather notes on the process and steps I followed.
|
||||
|
||||
<!-- truncate -->
|
||||
|
||||
## Installation
|
||||
|
||||
There is documentation on the installation process at [Hetzner](https://community.hetzner.com/tutorials/install-and-configure-proxmox_ve). There are options, and I chose the first one - installing Proxmox VE on Debian.
|
||||
So, according to the docs, I booted a [Rescue System](https://docs.hetzner.com/robot/dedicated-server/troubleshooting/hetzner-rescue-system/) and installed Debian with the [installimage](https://docs.hetzner.com/robot/dedicated-server/operating-systems/installimage/).
|
||||
Then I followed the [guide from Proxmox Wiki](https://pve.proxmox.com/wiki/Install_Proxmox_VE_on_Debian_13_Trixie) to install Proxmox VE. There is a step where you first install a new kernel and reboot to activate it. I was forced to reboot the server twice to make it appear online again. Have no idea why.
|
||||
|
||||
## Network
|
||||
|
||||
Now to the hard part.
|
||||
The initial plan was to have all LXCs in a single local network to allow internal communication. Also, the host and some containers should have public IPs for external access.
|
||||
|
||||
I reviewed a lot of network configuration options around the internet and chose a bridged setup with masquerading (NAT).
|
||||
|
||||
For the network I edited `/etc/network/interfaces` like this:
|
||||
|
||||
```
|
||||
auto lo
|
||||
|
||||
iface lo inet loopback
|
||||
|
||||
iface lo inet6 loopback
|
||||
|
||||
iface enp5s0 inet manual
|
||||
|
||||
iface enp5s0 inet6 manual
|
||||
|
||||
auto vmbr0
|
||||
iface vmbr0 inet static
|
||||
address <Public IPv4>/26
|
||||
gateway <Public IPv4 gateway>
|
||||
pointopoint <Public IPv4 gateway>
|
||||
bridge-ports enp5s0
|
||||
bridge-stp off
|
||||
bridge-fd 0
|
||||
up route add -net <Public IPv4 gateway - 1> netmask 255.255.255.192 gw <Public IPv4 gateway> dev vmbr0
|
||||
post-up echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||
post-up echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
|
||||
post-up iptables -t nat -A PREROUTING -i vmbr0 -p tcp -d <Public IPv4> --dport 21074 -j DNAT --to 192.168.50.3
|
||||
post-down iptables -t nat -D PREROUTING -i vmbr0 -p tcp -d <Public IPv4> --dport 21074 -j DNAT --to 192.168.50.3
|
||||
|
||||
iface vmbr0 inet6 static
|
||||
address <Public IPv6>/64
|
||||
gateway <Public IPv6 gateway>
|
||||
|
||||
auto vmbr1
|
||||
iface vmbr1 inet static
|
||||
address 192.168.50.1/24
|
||||
netmask 255.255.255.0
|
||||
bridge-ports none
|
||||
bridge-stp off
|
||||
bridge-fd 0
|
||||
post-up iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1
|
||||
post-down iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1
|
||||
post-up iptables -t nat -A POSTROUTING -s '192.168.50.0/24' -o vmbr0 -j MASQUERADE
|
||||
post-down iptables -t nat -D POSTROUTING -s '192.168.50.0/24' -o vmbr0 -j MASQUERADE
|
||||
```
|
||||
|
||||
Let's see what's going on in here.
|
||||
|
||||
For the containers that don't require a public IP, I choose `vbmr1` bridge, set some IP from the local IP range (192.168.50.x), and use host local IP as a gateway:
|
||||
|
||||

|
||||
|
||||
For LXCs that require a separate public IP, I choose the `vmbr0` bridge, set the IP, gateway, and MAC address provided by Hetzner.
|
||||
|
||||

|
||||
|
||||
The route with the `<Public IPv4 gateway - 1>` is a route from the official [Hetzner docs](https://community.hetzner.com/tutorials/install-and-configure-proxmox_ve). That doc was updated while I was writing this post, and the line was removed from the example network configuration there. Some network configuration guides across the internet still have it, so I'm leaving it as is for now. If your public IP gateway is, for example, 67.11.34.23, the route will be
|
||||
|
||||
```
|
||||
up route add -net 67.11.34.22 netmask 255.255.255.192 gw 67.11.34.23 dev vmbr0
|
||||
```
|
||||
|
||||
The next lines enable IP forwarding across networks:
|
||||
|
||||
```
|
||||
post-up echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||
post-up echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
|
||||
```
|
||||
|
||||
And here is an example of forwarding a port `21074` from the public IP to the local IP of a single LXC:
|
||||
|
||||
```
|
||||
post-up iptables -t nat -A PREROUTING -i vmbr0 -p tcp -d <Public IPv4> --dport 21074 -j DNAT --to 192.168.50.3
|
||||
post-down iptables -t nat -D PREROUTING -i vmbr0 -p tcp -d <Public IPv4> --dport 21074 -j DNAT --to 192.168.50.3
|
||||
```
|
||||
|
||||
For the local network `vmbr1` the next lines are needed to make Proxmox Firewall work properly:
|
||||
|
||||
```
|
||||
post-up iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1
|
||||
post-down iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1
|
||||
```
|
||||
|
||||
### Network security
|
||||
|
||||
Ports `8006`, `22`, and `8007` are opened on the Proxmox host in the Proxmox firewall, but explicitly closed in the Hetzner firewall. That way, I can use Proxmox VE and PBS web UI only from the Tailscale network, but, in case of an emergency or misconfiguration, I can open critical ports in Hetzner firewall to get access using server's public IP.
|
||||
|
||||
## Public Reverse Proxy
|
||||
|
||||
I know it is not the best practice to install anything directly on a Proxmox host, but this looks like a perfect decision, as it already has a public IP address, and I can't unassign it because this is the only way to access the server in case of failure.
|
||||
So I decided to install Caddy directly to a Proxmox Host and open ports `80` and `443` to it. Caddy handles requests and then proxies them to the local IPs of LXCs:
|
||||
|
||||
```
|
||||
git.nicelycomposed.codes {
|
||||
reverse_proxy 192.168.50.3:3000
|
||||
}
|
||||
```
|
||||
|
||||
## Tailscale and Internal Reverse Proxy
|
||||
|
||||
They call it "bastion host". A single point of connecting to your internal resources that you don't want to expose publicly. I have an LXC for this with Tailscale and another instance of Caddy installed. Let's see how it works on an example.
|
||||
|
||||
I want to securely connect to the Proxmox VE web UI using a valid SSL certificate at `https://pve.int.example.com`.
|
||||
|
||||
First, to make Tailscale work inside an LXC, additional configuration should be made on the Proxmox host. LXC configuration file can be found at `/etc/pve/lxc/<CT_ID>.conf`. Those two lines should be added to the file:
|
||||
|
||||
```
|
||||
lxc.cgroup2.devices.allow: c 10:200 rwm
|
||||
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
|
||||
```
|
||||
|
||||
Then just restart the LXC.
|
||||
|
||||
Then, I installed Tailscale and Caddy on my "bastion host". Then I created a DNS record to point `pve.int.example.com` to an IP address of my bastion host in the Tailscale network:
|
||||
|
||||
```
|
||||
A pve.int.example.com 10.11.12.13
|
||||
```
|
||||
|
||||
Then, in the Caddy config, I proxied `pve.int.example.com` to the local IP of my Proxmox host and port `8006`, using HTTPS and skipping TLS verification because of Proxmox's default self-signed certificate:
|
||||
|
||||
```
|
||||
pve.int.example.com {
|
||||
reverse_proxy 192.168.50.1:8006 {
|
||||
transport http {
|
||||
tls
|
||||
tls_insecure_skip_verify
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now I'm able to point my browser to `https://pve.int.example.com` while connected to my Tailscale network.
|
||||
|
||||
:::warning
|
||||
As all ports are closed on the "bastion host" and it is not exposed publicly, the default ACME HTTP challenge will not work for Caddy to issue SSL certificates for internal resources. [DNS challenge](https://caddyserver.com/docs/json/admin/identity/issuers/acme/challenges/) should be used instead.
|
||||
:::
|
||||
|
||||
I can add other internal resources hosted on other LXCs in the same way:
|
||||
|
||||
```
|
||||
dockge.int.example.com {
|
||||
reverse_proxy 192.168.50.4:5000
|
||||
}
|
||||
|
||||
element-admin.int.example.com {
|
||||
reverse_proxy 192.168.50.8:8080
|
||||
}
|
||||
```
|
||||
|
||||
## Proxmox Backup Server
|
||||
|
||||
It was the simplest part. I know this is not the most recommended method, but my Proxmox Backup Server is [installed on the Proxmox VE host](https://pbs.proxmox.com/docs/installation.html#install-proxmox-backup-server-on-proxmox-ve).
|
||||
After installing it and configuring it to use [Backblaze S3 storage](https://pbs.proxmox.com/docs/storage.html#datastores-with-s3-backend), I just added it as a storage to Proxmox VE using the local IP of the host.
|
||||
|
||||

|
||||
|
||||
## Conclusion
|
||||
Have no idea why everyone wants to write a conclusion for each post nowadays. It looks like a forced conclusion to a school physics problem in most cases: "Solving this problem of moving trains, we found out that trains can move".
|
||||
|
||||
Have a nice tinkering.
|
||||
|
||||
<BlogDiscussions mastodonLink="https://techhub.social/@estevez/115332360350515438"/>
|
BIN
blog/2025-10-07-proxmox-ve-on-hetzner-dedicated-server/pbs.png
Normal file
BIN
blog/2025-10-07-proxmox-ve-on-hetzner-dedicated-server/pbs.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 77 KiB |
BIN
blog/2025-10-07-proxmox-ve-on-hetzner-dedicated-server/vmbr0.png
Normal file
BIN
blog/2025-10-07-proxmox-ve-on-hetzner-dedicated-server/vmbr0.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 78 KiB |
BIN
blog/2025-10-07-proxmox-ve-on-hetzner-dedicated-server/vmbr1.png
Normal file
BIN
blog/2025-10-07-proxmox-ve-on-hetzner-dedicated-server/vmbr1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 90 KiB |
@@ -99,13 +99,14 @@ const config = {
|
||||
},
|
||||
items: [
|
||||
{ to: '/blog', label: 'Blog', position: 'left' },
|
||||
{ to: '/selfhosted-list', label: 'Self-hosted List', position: 'left' },
|
||||
],
|
||||
},
|
||||
footer: {
|
||||
style: 'dark',
|
||||
style: 'light',
|
||||
links: [
|
||||
{
|
||||
title: 'This site sections',
|
||||
title: 'This site sections:',
|
||||
items: [
|
||||
{
|
||||
label: 'Blog',
|
||||
@@ -121,7 +122,19 @@ const config = {
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
title: 'Feel free to contact me with questions and/or criticisms:',
|
||||
items: [
|
||||
{
|
||||
label: 'Matrix',
|
||||
to: 'https://matrix.to/#/@yehor:vi.place',
|
||||
},
|
||||
{
|
||||
label: 'Mastodon',
|
||||
to: 'https://techhub.social/@estevez',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
],
|
||||
copyright: `Copyright © ${new Date().getFullYear()} Yehor Vialov.`,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "yevi-org",
|
||||
"version": "0.2.1",
|
||||
"version": "0.4.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"docusaurus": "docusaurus",
|
||||
|
30
src/components/BlogDiscussions/index.js
Normal file
30
src/components/BlogDiscussions/index.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import styles from './styles.module.css';
|
||||
|
||||
const MatrixIcon = require('@site/static/icons/matrix.svg').default;
|
||||
|
||||
const MastodonIcon = require('@site/static/icons/mastodon.svg').default;
|
||||
|
||||
export default function BlogDiscussions({ mastodonLink }) {
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<section className={styles.blogDiscussions}>
|
||||
|
||||
<a className={styles.blogDiscussions_container} href="https://matrix.to/#/%23yevi-org-pub:vi.place" target='_blank'>
|
||||
<MatrixIcon fill="currentColor" className={styles.icon} role="img" />
|
||||
<span className={styles.linkText}>Discuss on Matrix</span>
|
||||
</a>
|
||||
|
||||
{mastodonLink ? (
|
||||
<a className={styles.blogDiscussions_container} href={mastodonLink} target='_blank'>
|
||||
<MastodonIcon fill="currentColor" className={styles.icon} role="img" />
|
||||
<span className={styles.linkText}>Discuss on Mastodon</span>
|
||||
</a>
|
||||
) : (
|
||||
null
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
29
src/components/BlogDiscussions/styles.module.css
Normal file
29
src/components/BlogDiscussions/styles.module.css
Normal file
@@ -0,0 +1,29 @@
|
||||
.blogDiscussions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
flex-wrap: wrap;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.blogDiscussions_container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.linkText {
|
||||
width: 12rem;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.icon path {
|
||||
fill: var(--ifm-heading-color) !important;
|
||||
}
|
@@ -39,7 +39,17 @@ const FeatureList = [
|
||||
Svg: require('@site/static/icons/matrix.svg').default,
|
||||
description: (
|
||||
<>
|
||||
If you want to chat
|
||||
If you want to chat (with end-to-end encryption)
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Pixelfed',
|
||||
link: 'https://pixelfed.social/estevez',
|
||||
Svg: require('@site/static/icons/pixelfed.svg').default,
|
||||
description: (
|
||||
<>
|
||||
For some photography
|
||||
</>
|
||||
),
|
||||
},
|
||||
|
@@ -9,15 +9,31 @@
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
@media (max-width: 996px) {
|
||||
.featureBlock {
|
||||
--ifm-col-width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.featureBlock {
|
||||
--ifm-col-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.featureSvg {
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.featureSvg path {
|
||||
fill: var(--ifm-link-color) !important;
|
||||
}
|
||||
|
||||
.featureBlock:hover .featureSvg path {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.featureLink {
|
||||
display: contents;
|
||||
color: unset;
|
||||
@@ -31,4 +47,8 @@
|
||||
.featureLink:visited {
|
||||
text-decoration: none;
|
||||
color: unset;
|
||||
}
|
||||
|
||||
.featureLink p {
|
||||
color: var(--ifm-color-secondary-contrast-foreground);
|
||||
}
|
@@ -11,12 +11,19 @@
|
||||
--ifm-color-primary-darker: #471947;
|
||||
--ifm-color-primary-darkest: #3b143b;
|
||||
--ifm-color-primary-light: #3b143b;
|
||||
--ifm-color-primary-lighter: #612161;
|
||||
--ifm-color-primary-lightest: #6d266d;
|
||||
--ifm-color-primary-lighter: #712471;
|
||||
--ifm-color-primary-lightest: #9d389d;
|
||||
--ifm-code-font-size: 95%;
|
||||
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
|
||||
--ifm-footer-link-hover-color: #b07ab0;
|
||||
--ifm-link-color: var(--ifm-color-primary-lightest)
|
||||
--ifm-link-color: var(--ifm-color-primary-lightest);
|
||||
--ifm-global-shadow-lw: 0 1px 2px 0 #541d5428;
|
||||
--ifm-heading-color: #575757;
|
||||
}
|
||||
|
||||
.hero--primary {
|
||||
--ifm-hero-background-color: transparent;
|
||||
--ifm-hero-text-color: var(--ifm-font-color-base);
|
||||
}
|
||||
|
||||
/* For readability concerns, you should choose a lighter palette in dark mode. */
|
||||
@@ -30,16 +37,14 @@
|
||||
--ifm-color-primary-lightest: #d1b2d1;
|
||||
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
|
||||
--ifm-link-color: var(--ifm-color-primary-darker);
|
||||
--ifm-heading-color: #919191;
|
||||
}
|
||||
|
||||
@media (max-width: 996px) {
|
||||
.col {
|
||||
--ifm-col-width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.col {
|
||||
--ifm-col-width: 100%;
|
||||
}
|
||||
h1 a,
|
||||
h2 a,
|
||||
h3 a,
|
||||
h4 a,
|
||||
h5 a,
|
||||
h6 a {
|
||||
color: var(--ifm-heading-color) !important;
|
||||
}
|
101
src/pages/selfhosted-list.md
Normal file
101
src/pages/selfhosted-list.md
Normal file
@@ -0,0 +1,101 @@
|
||||
---
|
||||
title: Self-hosted List
|
||||
description: A list of services I'm currently hosting for myself, family and friends
|
||||
---
|
||||
|
||||
A list of services I'm currently hosting for myself, family and friends. Grouped by access levels and sorted by gut feeling.
|
||||
|
||||
## Publicly accessible
|
||||
|
||||
Services anyone on the internet can access.
|
||||
|
||||
### This website (Docusaurus)
|
||||
[Service](https://www.yevi.org) | [Product](https://docusaurus.io)
|
||||
|
||||
This static site generator is mainly designed for documentation, but has a blog feature, pages, Markdown, and MDX support. When I decided to leave Ghost behind for something database-less, Markdown-ish, and stored in git, I checked different similar projects, like Hugo, for example. Docusaurus had the cleanest documentation, nicest look, and a very detailed example of a working website once you set it up. Keep it up, Docusaurus!
|
||||
|
||||
### Gitea
|
||||
[Service](https://git.nicelycomposed.codes) | [Product](https://about.gitea.com)
|
||||
|
||||
When I decided to self-host a Git server, GitLab was the first option I tried, as it was familiar to me from my day job. I launched it on an old Intel NUC with only 2 GB of memory... And started to search for lighter alternatives. So here I am with a Gitea instance that hosts my old repositories from GitHub, as well as a lot of private repos with different stuff. Gitea is fast, lightweight, single-binary, and has everything I need, including GitHub-compatible CI/CD.
|
||||
|
||||
## Restricted
|
||||
|
||||
Services exposed to the internet, but require authorization.
|
||||
|
||||
### Plausible Analytics
|
||||
[Product](https://plausible.io)
|
||||
|
||||
Google Analytics was my one-stop service for a long time, but it slowly enshittified into a huge, overcomplicated pile of pages and graphs you can hardly understand. Lack of fine documentation and constant requests to a user to migrate, fix, review, or reconfigure something didn't add any comfort in using it, while "Easy to use and privacy-friendly Google Analytics alternative" became more and more popular Google Search request.
|
||||
[Umami](https://umami.is) was the first thing I tried. It was almost perfect service for my needs until I faced the need to use API calls to send events from my pet project app. So now I'm with Plausible, and it looks like it is here to stay.
|
||||
|
||||
### Plane
|
||||
[Product](https://plane.so/project-management)
|
||||
|
||||
The only service I'm not happy with, but still using. Plane is slowly evolving into Jira. They even launched their own Confluence recently. While it is still a good project management software, I need something simpler for my needs. And there are some alternatives, but I have my pet-project tickets in Plane, and the migration will be a painful manual job. I'm not ready for this yet.
|
||||
|
||||
### Kan.bn
|
||||
[Product](https://kan.bn)
|
||||
|
||||
It is a simple but very nice kanban board. I'm not sure I will replace Plane with it, but I'm using it now as a personal to-do list. And for my homelab stuff, I'll never finish.
|
||||
|
||||
### Outline
|
||||
[Product](https://www.getoutline.com)
|
||||
|
||||
Need to store personal notes, family recipes, or a team’s knowledge base? Get an Outline in any of those cases and in other cases as well. The thing is so simple yet powerful and nice looking, I recommend setting it up even if you think you don't need it. As a bonus, mobile UI feels like a native app. My dream note-taking and knowledge-holding software looks and feels like Outline, has its collaboration features, but stores all notes locally like Obsidian does. For offline access at any time. Did you find something like this yet? Or maybe developing right now? No? Then I'll stick to the Outline for now.
|
||||
|
||||
### Yamtrack
|
||||
[Product](https://github.com/FuzzyGrim/Yamtrack)
|
||||
|
||||
Not that I have a bad memory. It's just a way to know what to watch next, and track things just because you can. Tracking TV shows, for example, becomes especially relevant after COVID and author strikes, when the pauses between some shows' seasons stretched to 3-5 years. I was a Trakt VIP user for a considerable amount of time. And before that, I checked several self-hosted alternatives, and they were all raw, ugly, and unstable. Now Yamtrack looks and feels like a polished product, so there is no reason to pay for Trakt left.
|
||||
|
||||
### AdventureLog
|
||||
[Product](https://adventurelog.app)
|
||||
|
||||
I liked the idea of Google location history very much. You were able to see routes you traveled and photos you took. After degoogling, all I had as a travel log was a map with all my photos. In Synology Photos at first, and in iCloud after. I also tried to use OwnTracks. Even extracted my past travels from my photos' GPS data and imported them there, but that was just lines and points with no context or additional information. I didn't even think that there was such a wonderful thing as AdventureLog. Locations, trips, stats, and more! You can log your travels as detailed as you wish and even plan future ones. Logging adventures from the past is a manual process, but it's a nice way to review and recall all the places you were and all the adventures you had.
|
||||
|
||||
### Authentik
|
||||
[Product](https://goauthentik.io)
|
||||
|
||||
There are two types of documentation. Actually, three, but the third one is a documentation absence. The first type wants you to feel like a smart person. It is clean, detailed, and up to date. The second type wants you to suffer and feel miserable. When I was choosing an identity provider for my self-hosting services, I picked Authentik because I like to feel smart.
|
||||
|
||||
### Synapse
|
||||
[Matrix](https://matrix.org) | [Element](https://element.io)
|
||||
Synapse is a Matrix server. Matrix is a protocol specification of a decentralised communication platform. But Synapse developed my Element. Element is an organisation that... well, it's complicated. Also, while hosting a Matrix server is not a trivial task, it's the most exciting thing to host. Because come on! It's a chat platform with video calls support, mobile and desktop apps, web clients, and end-to-end encryption! It's not just a web app with a single user - you. Something is continuously happening on the server; it consumes resources, connects to other similar servers, breaks when misconfigured, and makes your life engaging. The hardest part is to lure your friends and family, though.
|
||||
|
||||
### Other
|
||||
- **Matrix Authentication Service**
|
||||
- **Home Assistant**
|
||||
- **Plex**
|
||||
- **n8n**
|
||||
- **FreshRSS**
|
||||
|
||||
## Internal
|
||||
|
||||
Services accessible only from my home network or Tailnet.
|
||||
|
||||
- Zoraxy
|
||||
- Caddy
|
||||
- Grafana
|
||||
- Prometheus
|
||||
- InfuxDB
|
||||
- Dockge
|
||||
- Studio Code Server
|
||||
- Gitea Action Runner
|
||||
- Element Admin
|
||||
- Sonarr
|
||||
- Radarr
|
||||
- Prowlarr
|
||||
- qBittorrent
|
||||
- Proxmox Backup Server
|
||||
|
||||
## Operating Systems
|
||||
|
||||
- TrueNAS Community Edition
|
||||
- Proxmox VE
|
||||
|
||||
## Tools
|
||||
|
||||
- Tailscale
|
||||
|
||||
<BlogDiscussions />
|
13
src/theme/MDXComponents.js
Normal file
13
src/theme/MDXComponents.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
// Import the original mapper
|
||||
import MDXComponents from '@theme-original/MDXComponents';
|
||||
//Import custom component
|
||||
import BlogDiscussions from '@site/src/components/BlogDiscussions';
|
||||
|
||||
export default {
|
||||
// Re-use the default mapping
|
||||
...MDXComponents,
|
||||
// Map the "<BlogDiscussions>" tag to our BlogDiscussions component
|
||||
// `BlogDiscussions` will receive all props that were passed to `<BlogDiscussions>` in MDX
|
||||
BlogDiscussions,
|
||||
};
|
7
static/icons/pixelfed.svg
Normal file
7
static/icons/pixelfed.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="-10 -5 1034 1034" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" fill="#000000">
|
||||
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||
|
After Width: | Height: | Size: 845 B |
Reference in New Issue
Block a user