<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:base="https://www.yevi.org/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    
    <title>Posts from Yehor</title>
    <link>https://www.yevi.org/</link>
    <atom:link href="https://www.yevi.org/feed.xml" rel="self" type="application/rss+xml" />
    <description>Tech notes and projects posted by Yehor Vialov on his personal website.</description>
    <language>en</language>
    <item>
      <title>Daily Matrix and Mastodon backups with zero downtime</title>
      <link>https://www.yevi.org/notes/daily-matrix-and-mastodon-backups-with-zero-downtime/</link><description>&lt;p&gt;As I now run my own &lt;a href=&quot;https://mastodon.glitchy.social&quot;&gt;Mastodon instance&lt;/a&gt; &lt;s&gt;where anyone can request registration&lt;/s&gt;, there are goals to achieve with it. For example, the &lt;a href=&quot;https://joinmastodon.org/covenant&quot;&gt;Mastodon Server Covenant&lt;/a&gt;. There is &amp;quot;Daily backups&amp;quot; among other requirements, and today&#39;s story is about that. It involves Proxmox Backup Server, Ansible, and Tailscale as the three pillars of &amp;quot;Daily Matrix and Mastodon backups with zero downtime.&amp;quot;&lt;/p&gt;
&lt;!-- truncate --&gt;
&lt;p&gt;The simplest way to back up my Mastodon and Matrix servers, which run in a separate LXCs, was to set up snapshot-type backups on the Proxmox datacenter level and be happy, but snapshotting the database files is not the right way. You can catch your Postgres in the middle of the transaction and leave it corrupted after the restore. The right way was to make a dump of the databases, for example, with &lt;a href=&quot;https://www.postgresql.org/docs/current/app-pgdump.html&quot;&gt;&lt;code&gt;pg_dump&lt;/code&gt;&lt;/a&gt;, because &amp;quot;It generates a consistent snapshot of the database at the time the command is executed, even if the database is being used concurrently, without blocking other users.&amp;quot;&lt;/p&gt;
&lt;p&gt;But what about the other files? Configuration files, secrets, media storage? Sure, we can &lt;code&gt;rsync&lt;/code&gt; them off the server along with the database dump and be happy, but what about backup versioning, retention, and deduplication? As I already had a Proxmox Backup Server to handle those, my plan for backing up a server was simple:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Make a database dump to the file system of the server&lt;/li&gt;
&lt;li&gt;Make a snapshot-type backup of the server to the PBS&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This way, I&#39;d have consistent daily backups with a database dump and all needed files. And the restore procedure would consist of the same two steps, but in reverse:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Restore the server from PBS&lt;/li&gt;
&lt;li&gt;Restore the database from the dump on the server&#39;s file system&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I&#39;m not claiming to be an inventor of something new; I just think it is a good way. Loosing or corrupting several media files during this process is far less critical as missing database records or ending up with a corrupted database.&lt;/p&gt;
&lt;p&gt;So I ended up with two separate tools to make a backup. First is a &lt;code&gt;pg_dump&lt;/code&gt; CLI tool on the server, and the second one is a Proxmox Node to perform an LXC backup. Syncing those two, relying only on the time and delays, felt like a bad idea, so I decided to use Ansible from another Proxmox LXC to connect to my servers, make a database dump, and trigger a Proxmox backup job.&lt;/p&gt;
&lt;h2&gt;Tailscale SSH&lt;/h2&gt;
&lt;p&gt;To make that work, I needed an SSH connection between my &amp;quot;guard&amp;quot; server with Ansible running and the other two servers to back up. Sure, I could generate new SSH keys, make an exchange, but I already had Tailscale on my Proxmox guests, so why not use &lt;a href=&quot;https://tailscale.com/kb/1193/tailscale-ssh&quot;&gt;Tailscale SSH&lt;/a&gt; for that?&lt;/p&gt;
&lt;p&gt;The hardest part of Tailscale SSH configuration is to configure access rules, because Tailscale SSH between Tailscale devices is disabled by default. To enable it, I created two ACL tags in Tailscale:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&amp;quot;ansible-control-node&amp;quot; to assign to the &amp;quot;guard&amp;quot; server&lt;/li&gt;
&lt;li&gt;&amp;quot;ansible-managed-node&amp;quot; to assign to the Matrix and Mastodon servers&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Then, in the Tailscale Access Control, I just created a new rule for Tailscale SSH to allow SSH connection from &amp;quot;ansible-control-node&amp;quot; to &amp;quot;ansible-managed-node&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/daily-matrix-and-mastodon-backups-with-zero-downtime/jLqKMFQ4mI-600.png&quot; alt=&quot;Tailscale ACL rule&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;1732&quot; height=&quot;422&quot; srcset=&quot;https://www.yevi.org/notes/daily-matrix-and-mastodon-backups-with-zero-downtime/jLqKMFQ4mI-600.png 600w, https://www.yevi.org/notes/daily-matrix-and-mastodon-backups-with-zero-downtime/jLqKMFQ4mI-1000.png 1000w, https://www.yevi.org/notes/daily-matrix-and-mastodon-backups-with-zero-downtime/jLqKMFQ4mI-1732.png 1732w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Playbook&lt;/h2&gt;
&lt;p&gt;On the &amp;quot;ansible-control-node&amp;quot;, I installed Ansible Core 2.19 (at the moment of writing), created a &lt;code&gt;playbook.yaml&lt;/code&gt; and an &lt;code&gt;inventory.yaml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After some research and documentation reading, my &lt;code&gt;playbook.yaml&lt;/code&gt; ended up with two plays.&lt;/p&gt;
&lt;h3&gt;Database dump&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;- name: Database dumps
  hosts: postgres
  remote_user: root

  tasks:
    - name: Dump all databases
      ansible.builtin.command: pg_dumpall -c -f /var/lib/postgresql/db_dump_all.sql
      become_user: postgres
      become: true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;hosts&lt;/code&gt; here is a group containing servers with installed PostgreSQL.&lt;/p&gt;
&lt;p&gt;I used the &lt;code&gt;ansible.builtin.command&lt;/code&gt; task to create a dump of all databases on the server to a file, becoming a &lt;code&gt;postgres&lt;/code&gt; user. This system user is created by default in Ubuntu or Debian when installing Postgres from the apt repository.&lt;/p&gt;
&lt;h3&gt;Proxmox backup&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;- name: Backup Proxmox nodes
  hosts: localhost

  tasks:
    - name: Backup matrix and mastodon node
      community.proxmox.proxmox_backup:
        api_user: ansible@pve
        api_password: ***************
        api_host: ***.***.***.***
        storage: pbs # PBS storage ID
        backup_mode: snapshot
        retention: keep-monthly=6, keep-weekly=4, keep-daily=7
        mode: include
        wait: true
        wait_timeout: 240 # Wait fro x seconds before throwing a timeout error
        vmids: ## Proxmo guest IDs to backup
          - 109
          - 111
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;hosts&lt;/code&gt; here is a &lt;code&gt;localhost&lt;/code&gt; because we will trigger the Proxmox backup job through the Proxmox API.&lt;/p&gt;
&lt;p&gt;I created a separate user for this task on my Proxmox VE, giving him the &lt;code&gt;VM.Backup&lt;/code&gt; role for selected guests and &lt;code&gt;Datastore.Allocate&lt;/code&gt; plus &lt;code&gt;Datastore.AllocateSpace&lt;/code&gt; for the PBS storage.&lt;/p&gt;
&lt;h2&gt;Schedule&lt;/h2&gt;
&lt;p&gt;The last step was to run this playbook daily. I used a cron job. As the path to apps installed with Python was added to the &lt;code&gt;PATH&lt;/code&gt; environment variable through the &lt;code&gt;.bashrc&lt;/code&gt;, it is not available for the crontab, so I used a full path to the &lt;code&gt;ansible-playbook&lt;/code&gt;. Use &lt;code&gt;whereis ansible-playbook&lt;/code&gt; to get the full path.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;0 1 * * *  /root/.local/bin/ansible-playbook /root/ansible/playbook.yaml -i /root/ansible/inventory.yaml &amp;gt; /var/log/backups.log 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This job will be executed every day at 01:00 and write the result of the execution to &lt;code&gt;/var/log/backups.log&lt;/code&gt;. The log file will be rewritten in this case. If you want to append the log on each job execution, change &lt;code&gt;&amp;gt; /var/log/backups.log&lt;/code&gt; to &lt;code&gt;&amp;gt;&amp;gt; /var/log/backups.log&lt;/code&gt;.&lt;/p&gt;
</description><pubDate>Mon, 24 Nov 2025 00:00:00 +0000</pubDate>
      <dc:creator>Yehor Vialov</dc:creator>
      <guid>https://www.yevi.org/notes/daily-matrix-and-mastodon-backups-with-zero-downtime/</guid>
    </item>
    <item>
      <title>Glitchy.Social. The beginning</title>
      <link>https://www.yevi.org/projects/glitchy-social-the-beginning/</link><description>&lt;p&gt;I&#39;ve decided that the computing power of my Hetzner dedicated server allows me to provide some services to others. This is where Glitchy.Social has started.&lt;/p&gt;
&lt;!-- truncate --&gt;
&lt;p&gt;I already had the Synapse Matrix server hosted in my personal domain. And I thought that it was stable enough for me to allow other users to register. With registration tokens, I&#39;ll provide, of course. But my personal domain represents a part of my second name. &amp;quot;Who wants a Matrix account in the domain that represents a part of someone&#39;s second name?&amp;quot; - I thought.&lt;/p&gt;
&lt;p&gt;That&#39;s how my Matrix server has migrated to a newly purchased &lt;code&gt;glitchy.social&lt;/code&gt; domain. Actually, that was not a migration, but rather starting from scratch. I dropped the database of Synapse and Matrix Authentication Service, changed the homeserver name in all configs, and started fresh.&lt;/p&gt;
&lt;p&gt;You can chat with me now at &lt;a href=&quot;https://matrix.to/#/@yehor:glitchy.social&quot;&gt;@yehor:glitchy.social&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Then I started to think about how people will know how to join. Or even will know about Glitchy.Social itself? &amp;quot;I need a landing page. Or a website!&amp;quot; - I concluded.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.yevi.org/&quot;&gt;yevi.org&lt;/a&gt; is a Dcusaurus-powered website. And I really like its flexibility. It is as simple as adding or editing markdown files, but at the same time, you can write your own JavaScript components using ReactJS. Isn&#39;t it cool?&lt;/p&gt;
&lt;p&gt;So I present you today a shiny result of a one-night work: &lt;a href=&quot;https://www.glitchy.social/&quot;&gt;www.glithcy.social&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;&amp;quot;Hey! That&#39;s a nice logo you have there,&amp;quot; you&#39;ll suddenly exclaim, at least in my head. And I appreciate that reaction, because it is not an AI-generated slop. It is several hours of Inkscape with two project managers beside me: my wife and my daughter. I think they are still unsatisfied with my work, but we will improve it someday. For now, it is done, and it is nice in my opinion.&lt;/p&gt;
&lt;p&gt;The font is &lt;a href=&quot;https://fonts.google.com/specimen/Cal+Sans&quot;&gt;Cal Sans by Mark Davis&lt;/a&gt;, and the glitch effect is inspired by &lt;a href=&quot;https://logosbynick.com/create-glitch-effect-inkscape/&quot;&gt;a guide from Nick Saporito&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There is some info on the Matrix server on the website, primarily on how to request a registration token. Also, the Terms of Service, which I wrote by myself, felt disgusted, then asked AI for help, felt disgusted again, combined those two disgusting pieces of human and machine minds, and now you are forced to read it before joining. Sorry about that.&lt;/p&gt;
&lt;p&gt;This is how it started, and I&#39;m going to deploy a Mastodon instance in that nice domain as well. Maybe other services later, who knows.&lt;/p&gt;
</description><pubDate>Fri, 14 Nov 2025 00:00:00 +0000</pubDate>
      <dc:creator>Yehor Vialov</dc:creator>
      <guid>https://www.yevi.org/projects/glitchy-social-the-beginning/</guid>
    </item>
    <item>
      <title>Proxmox VE and PBS on Hetzner dedicated server</title>
      <link>https://www.yevi.org/notes/proxmox-ve-on-hetzner-dedicated-server/</link><description>&lt;p&gt;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&#39;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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;!-- truncate --&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;There is documentation on the installation process at &lt;a href=&quot;https://community.hetzner.com/tutorials/install-and-configure-proxmox_ve&quot;&gt;Hetzner&lt;/a&gt;. There are options, and I chose the first one - installing Proxmox VE on Debian.
So, according to the docs, I booted a &lt;a href=&quot;https://docs.hetzner.com/robot/dedicated-server/troubleshooting/hetzner-rescue-system/&quot;&gt;Rescue System&lt;/a&gt; and installed Debian with the &lt;a href=&quot;https://docs.hetzner.com/robot/dedicated-server/operating-systems/installimage/&quot;&gt;installimage&lt;/a&gt;.
Then I followed the &lt;a href=&quot;https://pve.proxmox.com/wiki/Install_Proxmox_VE_on_Debian_13_Trixie&quot;&gt;guide from Proxmox Wiki&lt;/a&gt; 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.&lt;/p&gt;
&lt;h2&gt;Network&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;I reviewed a lot of network configuration options around the internet and chose a bridged setup with masquerading (NAT).&lt;/p&gt;
&lt;p&gt;For the network I edited &lt;code&gt;/etc/network/interfaces&lt;/code&gt; like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;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 &amp;lt;Public IPv4&amp;gt;/26
        gateway &amp;lt;Public IPv4 gateway&amp;gt;
        pointopoint &amp;lt;Public IPv4 gateway&amp;gt;
        bridge-ports enp5s0
        bridge-stp off
        bridge-fd 0
        up route add -net &amp;lt;Public IPv4 gateway - 1&amp;gt; netmask 255.255.255.192 gw &amp;lt;Public IPv4 gateway&amp;gt; dev vmbr0
        post-up echo 1 &amp;gt; /proc/sys/net/ipv4/ip_forward
        post-up echo 1 &amp;gt; /proc/sys/net/ipv6/conf/all/forwarding
        post-up iptables -t nat -A PREROUTING -i vmbr0 -p tcp -d &amp;lt;Public IPv4&amp;gt; --dport 21074 -j DNAT --to 192.168.50.3
        post-down iptables -t nat -D PREROUTING -i vmbr0 -p tcp -d &amp;lt;Public IPv4&amp;gt; --dport 21074 -j DNAT --to 192.168.50.3

iface vmbr0 inet6 static
        address &amp;lt;Public IPv6&amp;gt;/64
        gateway &amp;lt;Public IPv6 gateway&amp;gt;

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 &#39;192.168.50.0/24&#39; -o vmbr0 -j MASQUERADE
        post-down iptables -t nat -D POSTROUTING -s &#39;192.168.50.0/24&#39; -o vmbr0 -j MASQUERADE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&#39;s see what&#39;s going on in here.&lt;/p&gt;
&lt;p&gt;For the containers that don&#39;t require a public IP, I choose &lt;code&gt;vbmr1&lt;/code&gt; bridge, set some IP from the local IP range (192.168.50.x), and use host local IP as a gateway:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/proxmox-ve-on-hetzner-dedicated-server/ihV4ZlAilv-600.png&quot; alt=&quot;LXC local network&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;1198&quot; height=&quot;534&quot; srcset=&quot;https://www.yevi.org/notes/proxmox-ve-on-hetzner-dedicated-server/ihV4ZlAilv-600.png 600w, https://www.yevi.org/notes/proxmox-ve-on-hetzner-dedicated-server/ihV4ZlAilv-1000.png 1000w, https://www.yevi.org/notes/proxmox-ve-on-hetzner-dedicated-server/ihV4ZlAilv-1198.png 1198w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;For LXCs that require a separate public IP, I choose the &lt;code&gt;vmbr0&lt;/code&gt; bridge, set the IP, gateway, and MAC address provided by Hetzner.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/proxmox-ve-on-hetzner-dedicated-server/pDdilHlGX8-600.png&quot; alt=&quot;LXC public network&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;1202&quot; height=&quot;536&quot; srcset=&quot;https://www.yevi.org/notes/proxmox-ve-on-hetzner-dedicated-server/pDdilHlGX8-600.png 600w, https://www.yevi.org/notes/proxmox-ve-on-hetzner-dedicated-server/pDdilHlGX8-1000.png 1000w, https://www.yevi.org/notes/proxmox-ve-on-hetzner-dedicated-server/pDdilHlGX8-1202.png 1202w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;The route with the &lt;code&gt;&amp;lt;Public IPv4 gateway - 1&amp;gt;&lt;/code&gt; is a route from the official &lt;a href=&quot;https://community.hetzner.com/tutorials/install-and-configure-proxmox_ve&quot;&gt;Hetzner docs&lt;/a&gt;. 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&#39;m leaving it as is for now. If your public IP gateway is, for example, 67.11.34.23, the route will be&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;up route add -net 67.11.34.22 netmask 255.255.255.192 gw 67.11.34.23 dev vmbr0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next lines enable IP forwarding across networks:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;post-up echo 1 &amp;gt; /proc/sys/net/ipv4/ip_forward
post-up echo 1 &amp;gt; /proc/sys/net/ipv6/conf/all/forwarding
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here is an example of forwarding a port &lt;code&gt;21074&lt;/code&gt; from the public IP to the local IP of a single LXC:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;post-up iptables -t nat -A PREROUTING -i vmbr0 -p tcp -d &amp;lt;Public IPv4&amp;gt; --dport 21074 -j DNAT --to 192.168.50.3
post-down iptables -t nat -D PREROUTING -i vmbr0 -p tcp -d &amp;lt;Public IPv4&amp;gt; --dport 21074 -j DNAT --to 192.168.50.3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the local network &lt;code&gt;vmbr1&lt;/code&gt; the next lines are needed to make Proxmox Firewall work properly:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;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
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Network security&lt;/h3&gt;
&lt;p&gt;Ports &lt;code&gt;8006&lt;/code&gt;, &lt;code&gt;22&lt;/code&gt;, and &lt;code&gt;8007&lt;/code&gt; 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&#39;s public IP.&lt;/p&gt;
&lt;h2&gt;Public Reverse Proxy&lt;/h2&gt;
&lt;p&gt;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&#39;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 &lt;code&gt;80&lt;/code&gt; and &lt;code&gt;443&lt;/code&gt; to it. Caddy handles requests and then proxies them to the local IPs of LXCs:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git.nicelycomposed.codes {
        reverse_proxy 192.168.50.3:3000
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Tailscale and Internal Reverse Proxy&lt;/h2&gt;
&lt;p&gt;They call it &amp;quot;bastion host&amp;quot;. A single point of connecting to your internal resources that you don&#39;t want to expose publicly. I have an LXC for this with Tailscale and another instance of Caddy installed. Let&#39;s see how it works on an example.&lt;/p&gt;
&lt;p&gt;I want to securely connect to the Proxmox VE web UI using a valid SSL certificate at &lt;code&gt;https://pve.int.example.com&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;First, to make Tailscale work inside an LXC, additional configuration should be made on the Proxmox host. LXC configuration file can be found at &lt;code&gt;/etc/pve/lxc/&amp;lt;CT_ID&amp;gt;.conf&lt;/code&gt;. Those two lines should be added to the file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then just restart the LXC.&lt;/p&gt;
&lt;p&gt;Then, I installed Tailscale and Caddy on my &amp;quot;bastion host&amp;quot;. Then I created a DNS record to point &lt;code&gt;pve.int.example.com&lt;/code&gt; to an IP address of my bastion host in the Tailscale network:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A      pve.int.example.com      10.11.12.13
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, in the Caddy config, I proxied &lt;code&gt;pve.int.example.com&lt;/code&gt; to the local IP of my Proxmox host and port &lt;code&gt;8006&lt;/code&gt;, using HTTPS and skipping TLS verification because of Proxmox&#39;s default self-signed certificate:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pve.int.example.com {
        reverse_proxy 192.168.50.1:8006 {
                transport http {
                        tls
                        tls_insecure_skip_verify
                }
        }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I&#39;m able to point my browser to &lt;code&gt;https://pve.int.example.com&lt;/code&gt; while connected to my Tailscale network.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ As all ports are closed on the &amp;quot;bastion host&amp;quot; and it is not exposed publicly, the default ACME HTTP challenge will not work for Caddy to issue SSL certificates for internal resources. &lt;a href=&quot;https://caddyserver.com/docs/json/admin/identity/issuers/acme/challenges/&quot;&gt;DNS challenge&lt;/a&gt; should be used instead.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I can add other internal resources hosted on other LXCs in the same way:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dockge.int.example.com {
        reverse_proxy 192.168.50.4:5000
}

element-admin.int.example.com {
        reverse_proxy 192.168.50.8:8080
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Proxmox Backup Server&lt;/h2&gt;
&lt;p&gt;It was the simplest part. I know this is not the most recommended method, but my Proxmox Backup Server is &lt;a href=&quot;https://pbs.proxmox.com/docs/installation.html#install-proxmox-backup-server-on-proxmox-ve&quot;&gt;installed on the Proxmox VE host&lt;/a&gt;.
After installing it and configuring it to use &lt;a href=&quot;https://pbs.proxmox.com/docs/storage.html#datastores-with-s3-backend&quot;&gt;Backblaze S3 storage&lt;/a&gt;, I just added it as a storage to Proxmox VE using the local IP of the host.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/proxmox-ve-on-hetzner-dedicated-server/9hmE1O6FTy-600.png&quot; alt=&quot;PBS config&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;1202&quot; height=&quot;628&quot; srcset=&quot;https://www.yevi.org/notes/proxmox-ve-on-hetzner-dedicated-server/9hmE1O6FTy-600.png 600w, https://www.yevi.org/notes/proxmox-ve-on-hetzner-dedicated-server/9hmE1O6FTy-1000.png 1000w, https://www.yevi.org/notes/proxmox-ve-on-hetzner-dedicated-server/9hmE1O6FTy-1202.png 1202w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;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: &amp;quot;Solving this problem of moving trains, we found out that trains can move&amp;quot;.&lt;/p&gt;
&lt;p&gt;Have a nice tinkering.&lt;/p&gt;
</description><pubDate>Tue, 07 Oct 2025 00:00:00 +0000</pubDate>
      <dc:creator>Yehor Vialov</dc:creator>
      <guid>https://www.yevi.org/notes/proxmox-ve-on-hetzner-dedicated-server/</guid>
    </item>
    <item>
      <title>Make one Proxmox node to wake-on-lan another</title>
      <link>https://www.yevi.org/notes/make-one-proxmox-node-to-wol-another/</link><description>&lt;p&gt;Nothing is eternal, especially the relevance of documentation. I think help.ubuntu.com is an absolute winner, holding the largest number of outdated and irrelevant pages. But that&#39;s not a topic for today&#39;s post.
One of my Proxmox cluster nodes can&#39;t power itself on after the outage. But it supports wake-on-LAN, so I decided that another node could power it on. And the simplicity of this task was overrated by me.&lt;/p&gt;
&lt;!-- truncate --&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ Wake on LAN doesn&#39;t work across VLANs. Magic packets could be sent and received only inside a single subnet.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;The victim&lt;/h2&gt;
&lt;p&gt;First of all, even after enabling &amp;quot;Wake up on PCI event&amp;quot; or something in BIOS it was not working because WoL was still disabled on a software level. It can be checked with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ethtool enp1s0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Where &lt;code&gt;enp1s0&lt;/code&gt; is a physical network interface of a Proxmox node, not a bridge.&lt;/p&gt;
&lt;p&gt;There should be &lt;code&gt;Wake-on:&lt;/code&gt; setting among others. In my case it was &lt;code&gt;Wake-on: d&lt;/code&gt;, which means that wake-on-LAN is disabled, according to &lt;code&gt;ethtool&lt;/code&gt; documentation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;p  Wake on phy activity
u  Wake on unicast messages
m  Wake on multicast messages
b  Wake on broadcast messages
a  Wake on ARP
g  Wake on MagicPacket(tm)
s  Enable SecureOn(tm) password for MagicPacket(tm)
d  Disable (wake on nothing).  This option clears  all  previous options.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We need to set it to wake by the MagicPacket(tm).&lt;/p&gt;
&lt;h3&gt;for Proxmox 8 or older&lt;/h3&gt;
&lt;p&gt;We need to create a config file for this to be enabled on system start. But first we need to:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ip link show enp1s0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and write down our network device MAC address. Then create a file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;nano /etc/systemd/network/90-wakeonlan.link
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;with the next content:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ini&quot;&gt;[Match]
MACAddress=&amp;lt;mac-address-here&amp;gt;

[Link]
NamePolicy=kernel database onboard slot path
MACAddressPolicy=persistent
WakeOnLan=magic
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;for Proxmox 9&lt;/h3&gt;
&lt;p&gt;We need to create a systemd service that will be launched on system start and change the WOL setting with &lt;code&gt;ethtool&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Create a file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;nano /etc/systemd/system/wol.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;with the next content:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ini&quot;&gt;[Unit]
Description=Enable Wake On Lan

[Service]
Type=oneshot
ExecStart=/usr/sbin/ethtool --change enp1s0 wol g

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;changing &lt;code&gt;enp1s0&lt;/code&gt; to your interface name. Enable the service:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;systemctl daemon-reload &amp;amp;&amp;amp; systemctl enable wol.service
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;then&lt;/h3&gt;
&lt;p&gt;After that we need to reboot and check WOL status again:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ethtool enp1s0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now &lt;code&gt;Wake-on&lt;/code&gt; should be set to &lt;code&gt;g&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;The one who bothering&lt;/h2&gt;
&lt;p&gt;On another node we need to install an util that will be sending a magic packet:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;apt update
apt install etherwake
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can power the victim off and try to wake it with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;etherwake -i vmbr0 &amp;lt;mac_address&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Where &lt;code&gt;vmbr0&lt;/code&gt; is a bridge network interface of current Proxmox node, and &lt;code&gt;&amp;lt;mac_address&amp;gt;&lt;/code&gt; is a MAC address of the victim&#39;s physical network interface.&lt;/p&gt;
&lt;p&gt;If it works, we can now add a cron job to wake our victim upon current node startup, adding some delay to make sure the network is ready:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;crontab -e
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cron job line to add:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@reboot sleep 30s &amp;amp;&amp;amp; /usr/sbin/etherwake -i vmbr0 &amp;lt;mac_address&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
</description><pubDate>Wed, 21 May 2025 00:00:00 +0000</pubDate>
      <dc:creator>Yehor Vialov</dc:creator>
      <guid>https://www.yevi.org/notes/make-one-proxmox-node-to-wol-another/</guid>
    </item>
    <item>
      <title>WireGuard tunnel from Ubuntu VPS to HomeLab through UniFi VPN server</title>
      <link>https://www.yevi.org/notes/wireguard-tunnel-from-ubuntu-vps-to-homelab-through-unifi-vpn-server/</link><description>&lt;p&gt;I&#39;m not surprised people prefer Tailscale over WireGuard. Did you see the WireGuard &lt;a href=&quot;https://www.wireguard.com/quickstart/&quot;&gt;quick start guide&lt;/a&gt;? Did you try to connect using this guide? Anyway, I have a VPS on Hetzner I wanted to connect to my local network through a secure tunnel. Tailscale is nice, but why set up a new infrastructure when I have one inside my UniFi Cloud Gateway Ultra?&lt;/p&gt;
&lt;!-- truncate --&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ This guide will work for you only if you have a static WAN IP&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So first of all I went to &lt;em&gt;Settings -&amp;gt; VPN&lt;/em&gt; on my UniFi Cloud Gateway Ultra, switched to a &lt;em&gt;VPN Server&lt;/em&gt; tab, and added a new Wireguard VPN server. I let UniFi decide on the client&#39;s IP range.&lt;/p&gt;
&lt;p&gt;Then I added a client, which will be my VPS. And downloaded a WireGuard interface config file there:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/wireguard-tunnel-from-ubuntu-vps-to-homelab-through-unifi-vpn-server/-Z0zeDFuhT-600.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;790&quot; height=&quot;934&quot; srcset=&quot;https://www.yevi.org/notes/wireguard-tunnel-from-ubuntu-vps-to-homelab-through-unifi-vpn-server/-Z0zeDFuhT-600.png 600w, https://www.yevi.org/notes/wireguard-tunnel-from-ubuntu-vps-to-homelab-through-unifi-vpn-server/-Z0zeDFuhT-790.png 790w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then I connected to my VPS through SSH to make some Linux command line magic.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I need to mention here that I&#39;m writing this guide a long time after the actual setup. So unfortunately I can&#39;t remember the sources I found tips and tricks to make this work. But it works now for me and can help to make it work for you, my dear reader.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;On my VPS I have Ubuntu 24.04 installed.&lt;/p&gt;
&lt;p&gt;So, to the command line. Install WireGuard:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt install wireguard
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a config file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo nano /etc/wireguard/wg0.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Insert the configuration downloaded from the UniFi VPN client creation step:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ini&quot;&gt;[Interface]
PrivateKey = ***************************************
Address = 192.168.4.2/32
DNS = 192.168.4.1

[Peer]
PublicKey = ***************************************
AllowedIPs = 0.0.0.0/0
Endpoint = ***.***.**.**:51821
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now edit it to look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ini&quot;&gt;[Interface]
PrivateKey = ***************************************
Address = 192.168.4.2/32
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
ListenPort = 51820

[Peer]
PublicKey = ***************************************
AllowedIPs = 192.168.4.0/24,192.168.50.0/24,192.168.1.0/24
Endpoint = ***.***.**.**:51821
PersistentKeepalive = 10
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;PostUp&lt;/code&gt; and &lt;code&gt;PostDown&lt;/code&gt; are commands to be executed when the WireGuard network interface connects or disconnects. In my case, this is to add and remove &lt;code&gt;iptables&lt;/code&gt; rules for WireGuard traffic routing through the default network interface.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;AllowedIPs&lt;/code&gt; should be edited to your needs. Here is my needs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;192.168.4.0/24&lt;/code&gt; allows access to any IP address on the same subnet as the VPS would be placed after connecting&lt;/li&gt;
&lt;li&gt;&lt;code&gt;192.168.50.0/24&lt;/code&gt; allows access to any IP on my other subnet&lt;/li&gt;
&lt;li&gt;&lt;code&gt;192.168.1.0/24&lt;/code&gt; allows access to any IP on my main subnet (I&#39;m not sure about this, but anyway the access is blocked on the UniFi Firewall level)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These rules are loose because the Firewall on my UniFi Gateway blocks any cross-subnet traffic anyway allowing only limited communications.&lt;/p&gt;
&lt;p&gt;This should be enough to bring the connection up with &lt;code&gt;wg-quick&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo wg-quick up wg0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To see the status:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo wg show
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now to make it work through server reboots we need to down the interface:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo wg-quick down wg0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And up it back with a system service, also enabling it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo systemctl start wg-quick@wg0 &amp;amp;&amp;amp; sudo systemctl enable wg-quick@wg0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Further interface control should be performed through &lt;code&gt;systemctl&lt;/code&gt; as well like &lt;code&gt;systemctrl stop&lt;/code&gt;, &lt;code&gt;systemctl restart&lt;/code&gt; or &lt;code&gt;systemctl status&lt;/code&gt;.&lt;/p&gt;
</description><pubDate>Sat, 15 Feb 2025 00:00:00 +0000</pubDate>
      <dc:creator>Yehor Vialov</dc:creator>
      <guid>https://www.yevi.org/notes/wireguard-tunnel-from-ubuntu-vps-to-homelab-through-unifi-vpn-server/</guid>
    </item>
    <item>
      <title>Restart or shutdown your Proxmox node from Home Assistant automation</title>
      <link>https://www.yevi.org/notes/restart-or-shutdown-your-proxmox-node-from-home-assistant-automation/</link><description>&lt;p&gt;There is a &lt;a href=&quot;https://www.yevi.org/notes/monitor-usb-ups-connected-to-synology-nas-in-home-assistant&quot;&gt;UPS monitoring through NUT&lt;/a&gt; configured in my Home Assistant. So I decided my Home Assistant could shut down my homelab servers on a low UPS battery.&lt;/p&gt;
&lt;!-- truncate --&gt;
&lt;p&gt;We will use &lt;a href=&quot;https://pve.proxmox.com/wiki/Proxmox_VE_API&quot;&gt;Proxmox VE API&lt;/a&gt; and Home Assistant &lt;a href=&quot;https://www.home-assistant.io/integrations/rest_command/&quot;&gt;RESTful Command&lt;/a&gt; integration for this.&lt;/p&gt;
&lt;h2&gt;Proxmox user and permissions&lt;/h2&gt;
&lt;p&gt;Let&#39;s do things right and don&#39;t allow Home Assistant to log in with the root user to your Proxmox environment. We will create a &lt;em&gt;role&lt;/em&gt; first. Go to your &lt;em&gt;Datacenter&lt;/em&gt; view in Proxmox web UI choose &lt;em&gt;Permissions -&amp;gt; Roles&lt;/em&gt; and hit &lt;em&gt;Create&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;We will create a &lt;code&gt;PowerManager&lt;/code&gt; role with &lt;code&gt;Sys.PowerMgmt&lt;/code&gt; privileges. This will allow our user to execute shutdown/restart commands through Proxmox API, but nothing more.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/restart-or-shutdown-your-proxmox-node-from-home-assistant-automation/tHU6Vboziv-600.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;1504&quot; height=&quot;1106&quot; srcset=&quot;https://www.yevi.org/notes/restart-or-shutdown-your-proxmox-node-from-home-assistant-automation/tHU6Vboziv-600.png 600w, https://www.yevi.org/notes/restart-or-shutdown-your-proxmox-node-from-home-assistant-automation/tHU6Vboziv-1000.png 1000w, https://www.yevi.org/notes/restart-or-shutdown-your-proxmox-node-from-home-assistant-automation/tHU6Vboziv-1504.png 1504w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;Next, we will create a user. In Proxmox web UI go to &lt;em&gt;Permissions -&amp;gt; Users&lt;/em&gt; and hit &lt;em&gt;Add&lt;/em&gt;. Give him a username and password, and choose &lt;em&gt;&amp;quot;Proxmox VE authentication server&amp;quot;&lt;/em&gt; as a &lt;em&gt;Realm&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/restart-or-shutdown-your-proxmox-node-from-home-assistant-automation/UKO_CKqoHI-600.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;617&quot; height=&quot;338&quot; srcset=&quot;https://www.yevi.org/notes/restart-or-shutdown-your-proxmox-node-from-home-assistant-automation/UKO_CKqoHI-600.png 600w, https://www.yevi.org/notes/restart-or-shutdown-your-proxmox-node-from-home-assistant-automation/UKO_CKqoHI-617.png 617w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;Next, we will go to our &lt;em&gt;Datacenter&lt;/em&gt; view in Proxmox web UI choose &lt;em&gt;Permissions&lt;/em&gt; and hit &lt;em&gt;Add&lt;/em&gt;. Here, we will set &lt;code&gt;/nodes/&amp;lt;node_name&amp;gt;&lt;/code&gt; as the Path, select our newly created &lt;em&gt;User&lt;/em&gt; and our newly created &lt;em&gt;Role&lt;/em&gt; for him:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/restart-or-shutdown-your-proxmox-node-from-home-assistant-automation/v61yFXuXt--397.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;397&quot; height=&quot;208&quot;&gt;&lt;/p&gt;
&lt;p&gt;Lastly, we will need an API token to make API calls. For this, we will go to &lt;em&gt;Permissions -&amp;gt; API&lt;/em&gt; Tokens and click &lt;em&gt;Add&lt;/em&gt;. Choose our newly created user, give the token an ID, and disable &lt;em&gt;Privilege Separation&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/restart-or-shutdown-your-proxmox-node-from-home-assistant-automation/SbQluJtHkz-600.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;206&quot;&gt;&lt;/p&gt;
&lt;p&gt;After clicking &lt;em&gt;Add&lt;/em&gt; we will see the token. We need to write down the token ID and token itself to use it in Home Assistant later.&lt;/p&gt;
&lt;h2&gt;Home Assistant integration&lt;/h2&gt;
&lt;p&gt;Now to the Home Assistant. We will need to edit our &lt;code&gt;configuration.yaml&lt;/code&gt; file as currently, this is the only way to add RESTful Command integration. Here is an example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;rest_command:
  spacedock_one_shutdown:
    url: &amp;quot;https://192.168.99.3:8006/api2/json/nodes/spacedock-one/status&amp;quot;
    method: post
    headers:
      Authorization: PVEAPIToken=pwrmngr@pve!power=73892874-ad34-4b98-83e2-7be787f9bee3
    content_type: &amp;quot;application/x-www-form-urlencoded&amp;quot;
    payload: &amp;quot;command=shutdown&amp;quot;
    verify_ssl: false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;url&lt;/code&gt; should have the next format: &lt;code&gt;https://&amp;lt;proxmox node IP&amp;gt;:8006/api2/json/nodes/&amp;lt;proxmox node name&amp;gt;/status&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The authorization header should have the next format: &lt;code&gt;PVEAPIToken=&amp;lt;proxmox username&amp;gt;@pve!&amp;lt;API token ID&amp;gt;=&amp;lt;API token&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You can use &lt;code&gt;shutdown&lt;/code&gt; or &lt;code&gt;reboot&lt;/code&gt; as a &lt;code&gt;command&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After restarting Home Assistant you will have a new service available. In our example, it would be &lt;code&gt;rest_command.spacedock_one_shutdown&lt;/code&gt;. Calling it will do the trick.&lt;/p&gt;
</description><pubDate>Tue, 19 Dec 2023 00:00:00 +0000</pubDate>
      <dc:creator>Yehor Vialov</dc:creator>
      <guid>https://www.yevi.org/notes/restart-or-shutdown-your-proxmox-node-from-home-assistant-automation/</guid>
    </item>
    <item>
      <title>Monitor USB UPS connected to Synology NAS in Home Assistant</title>
      <link>https://www.yevi.org/notes/monitor-usb-ups-connected-to-synology-nas-in-home-assistant/</link><description>&lt;p&gt;There is a lot of automation you can do with this data, but today I want to write up a short guide on how to safely get the information from the UPS connected to your NAS via USB into the Home Assistant.&lt;/p&gt;
&lt;!-- truncate --&gt;
&lt;p&gt;There is a lot of automation you can do with this data, but today I want to write up a short guide on how to safely get the information from the UPS connected to your NAS via USB into the Home Assistant.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Your Synology NAS and Home Assistant should be in the same local network, or you&#39;ll need an additional network configuration that is not a subject of this post&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;UPS server&lt;/h2&gt;
&lt;p&gt;After connecting the UPS to the NAS we need to enable Synology NAS UPS server that will allow other machines to get UPS information over the local network. In your Synology DSM go to &lt;em&gt;Control Panel -&amp;gt; Hardware &amp;amp; Power -&amp;gt; UPS&lt;/em&gt; tab. Here you can set up a UPS connected to your NAS via USB (or via SNMP protocol if you have enterprise-grade UPS for data centers). Assuming you did that, it is time to enable the UPS server by checking the &lt;em&gt;&amp;quot;Enable network UPS server&amp;quot;&lt;/em&gt; checkbox.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/monitor-usb-ups-connected-to-synology-nas-in-home-assistant/x0-yeq32ZN-600.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;1016&quot; height=&quot;710&quot; srcset=&quot;https://www.yevi.org/notes/monitor-usb-ups-connected-to-synology-nas-in-home-assistant/x0-yeq32ZN-600.png 600w, https://www.yevi.org/notes/monitor-usb-ups-connected-to-synology-nas-in-home-assistant/x0-yeq32ZN-1000.png 1000w, https://www.yevi.org/notes/monitor-usb-ups-connected-to-synology-nas-in-home-assistant/x0-yeq32ZN-1016.png 1016w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;Also, you need to click the &lt;em&gt;&amp;quot;Permitted Synology NAS Devices&amp;quot;&lt;/em&gt; button and add your Home Assistant local IP address there:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/monitor-usb-ups-connected-to-synology-nas-in-home-assistant/f2d8-9wUJ8-516.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;516&quot; height=&quot;259&quot;&gt;&lt;/p&gt;
&lt;p&gt;After that, you need to make sure the port for the UPS server is open in the firewall on your NAS. Go to &lt;em&gt;Control Panel -&amp;gt; Security -&amp;gt; Firewall&lt;/em&gt; tab and click the &lt;em&gt;&amp;quot;Edit rules&amp;quot;&lt;/em&gt; button. Here you need to edit an existing rule for the &lt;em&gt;TCP&lt;/em&gt; protocol that has a list of apps in the &lt;em&gt;Ports&lt;/em&gt; column:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/monitor-usb-ups-connected-to-synology-nas-in-home-assistant/7iir8rMKne-600.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;798&quot; height=&quot;576&quot; srcset=&quot;https://www.yevi.org/notes/monitor-usb-ups-connected-to-synology-nas-in-home-assistant/7iir8rMKne-600.png 600w, https://www.yevi.org/notes/monitor-usb-ups-connected-to-synology-nas-in-home-assistant/7iir8rMKne-798.png 798w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;In the editing window click the Select button near &lt;em&gt;&amp;quot;Select from a list of built-in applications&amp;quot;&lt;/em&gt; and make sure the &lt;em&gt;UPS server&lt;/em&gt; is selected:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/monitor-usb-ups-connected-to-synology-nas-in-home-assistant/GK1AW-raXV-600.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;649&quot; height=&quot;427&quot; srcset=&quot;https://www.yevi.org/notes/monitor-usb-ups-connected-to-synology-nas-in-home-assistant/GK1AW-raXV-600.png 600w, https://www.yevi.org/notes/monitor-usb-ups-connected-to-synology-nas-in-home-assistant/GK1AW-raXV-649.png 649w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Home Assistant integration&lt;/h2&gt;
&lt;p&gt;The integration that will allow us to connect to the UPS server on Synology NAS is &lt;a href=&quot;https://www.home-assistant.io/integrations/nut/&quot;&gt;Network UPS Tools&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In your Home Assistant go to &lt;em&gt;Settings -&amp;gt; Devices &amp;amp; services&lt;/em&gt; and click the &lt;em&gt;&amp;quot;Add integration&amp;quot;&lt;/em&gt; button. Search for &amp;quot;nut&amp;quot; there and choose &lt;em&gt;&amp;quot;Network UPS Tools (NUT)&amp;quot;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/monitor-usb-ups-connected-to-synology-nas-in-home-assistant/5mop7hm9rt-482.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;482&quot; height=&quot;415&quot;&gt;&lt;/p&gt;
&lt;p&gt;In the configuration window that appeared you need to set your NAS&#39;s local IP address as a &lt;em&gt;Host&lt;/em&gt; and leave the &lt;em&gt;Port&lt;/em&gt; number unchanged.&lt;/p&gt;
&lt;p&gt;Hit &lt;em&gt;Submit&lt;/em&gt; and you are done.&lt;/p&gt;
</description><pubDate>Tue, 19 Dec 2023 00:00:00 +0000</pubDate>
      <dc:creator>Yehor Vialov</dc:creator>
      <guid>https://www.yevi.org/notes/monitor-usb-ups-connected-to-synology-nas-in-home-assistant/</guid>
    </item>
    <item>
      <title>Hosting Ghost Blog On Synology NAS</title>
      <link>https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/</link><description>&lt;p&gt;Do you want to know how and where RandomPlace is hosted? Or do you just want to know how to host a &lt;a href=&quot;https://ghost.org&quot;&gt;Ghost&lt;/a&gt; blog on your Synology NAS? I&#39;m here today to answer both questions.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This post is from the past. From far away times when this website had another domain, another name and another hosting place. Still, this guide can be useful.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;!-- truncate --&gt;
&lt;p&gt;It&#39;s been a long way to my current home lab configuration. There were several Intel NUCs, Nextcloud, and so on, but at the end (or just for now) I have Synology NAS and it allows me to host not only my personal cloud but other services as well. This blog is one of them.&lt;/p&gt;
&lt;p&gt;RandomPlace blog is running in the Docker container. In Synology DSM it is a &lt;a href=&quot;https://www.synology.com/en-us/dsm/feature/container-manager&quot;&gt;Container Manager&lt;/a&gt; package. To be honest, I&#39;m not a big fan of the everything-in-containers approach. Bare-metal installation is much better in terms of maintainability and flexibility, but it is clear why Docker containers become so popular across our home labs. It is much easier to get something up and running with a single command instead of resolving dependencies and configuring your OS. So when this blog was hosted on Intel NUC with Ubuntu OS, it was not in containers. Now, with a new-for-me DSM software, it is easier to get something up and running with a single command instead of resolving dependencies and configuring my OS =)&lt;/p&gt;
&lt;p&gt;I know there is a way to run NodeJS apps on Synology DSM, but this is something I need to discover. I&#39;m also not sure I&#39;m ready to SSH to my NAS and start to edit system files. Stability is the main reason why I&#39;m currently with Synology.&lt;/p&gt;
&lt;p&gt;Let&#39;s get back to our main topic. All further instructions are from DS423+ and DSM 7.2. But I&#39;m sure it would be the same for many other NASes.&lt;/p&gt;
&lt;h2&gt;Prepare&lt;/h2&gt;
&lt;p&gt;First of all, we will need a &lt;strong&gt;Container Manager&lt;/strong&gt; package to be installed from the Package Center&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/kJGJj7pcIr-600.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;1055&quot; height=&quot;674&quot; srcset=&quot;https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/kJGJj7pcIr-600.png 600w, https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/kJGJj7pcIr-1000.png 1000w, https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/kJGJj7pcIr-1055.png 1055w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then we will need some folders to be created. After installing a Container Manager, you will have a new shared folder created for you: &lt;code&gt;docker&lt;/code&gt;. A good place to put all your container&#39;s data in. We should create a new directory there with any name you want. For example &lt;code&gt;blog&lt;/code&gt;. This would be the root of our future project. Then, let&#39;s create a &lt;code&gt;content&lt;/code&gt; directory inside it:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/mwuM9XmG_9-226.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;226&quot; height=&quot;98&quot;&gt;&lt;/p&gt;
&lt;p&gt;Lastly, we will need to know the volume number where our directories are placed. For this open &lt;strong&gt;File Station&lt;/strong&gt;, right-click on the docker folder and choose &lt;strong&gt;Properties&lt;/strong&gt;. The &lt;strong&gt;Location&lt;/strong&gt; property in my case tells me that the docker folder is placed on a &lt;code&gt;volume1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/aaXQ1JBMe8-524.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;524&quot; height=&quot;205&quot;&gt;&lt;/p&gt;
&lt;h2&gt;The Database&lt;/h2&gt;
&lt;p&gt;The Ghost requires the MySQL database to work. For the database, we have two options. The first is to have a database as a separate docker container. This option is the simplest. Another one is to use the &lt;strong&gt;MariaDB&lt;/strong&gt; Synology package to have a single database service for all your projects. This will require us to install the &lt;strong&gt;phpMyAdmin&lt;/strong&gt; as well to create and manage databases and users. This option enables the ability for a simple database backup solution because the &lt;strong&gt;MariaDB&lt;/strong&gt; package with all data and settings could be included in &lt;strong&gt;Hyper Backup&lt;/strong&gt;. Also, we will avoid having several containers with the same database service in the future.&lt;/p&gt;
&lt;p&gt;With the second option, after installing &lt;strong&gt;MariaDB&lt;/strong&gt; and &lt;strong&gt;phpMyAdmin&lt;/strong&gt; from the &lt;strong&gt;Package Center&lt;/strong&gt; on our NAS, let&#39;s log in to &lt;strong&gt;phpMyAdmin&lt;/strong&gt; with the root user and password we set upon installation and create a new database for our blog and name it, for example, &amp;quot;my_blog&amp;quot;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/2RM8zNbZ_8-600.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;810&quot; height=&quot;346&quot; srcset=&quot;https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/2RM8zNbZ_8-600.png 600w, https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/2RM8zNbZ_8-810.png 810w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now we will need to create a user for this database. Choose your newly created database on the left and navigate to the &lt;strong&gt;Privileges&lt;/strong&gt; tab. You can find the &amp;quot;Add user account&amp;quot; link there.&lt;/p&gt;
&lt;p&gt;Selecting &amp;quot;Any host&amp;quot; for the &amp;quot;Host name&amp;quot; field upon user creation will allow the Ghost container to connect to the database even from the bridged network. But also allows connection from any host within your local network (assuming that you didn&#39;t open MySQL port to the public internet which you shouldn&#39;t do in any case).&lt;/p&gt;
&lt;p&gt;Don&#39;t forget to tick &amp;quot;Grant all privileges on database my_blog.&amp;quot; on the same user creation page.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/KteV1is0IO-600.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;660&quot; height=&quot;563&quot; srcset=&quot;https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/KteV1is0IO-600.png 600w, https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/KteV1is0IO-660.png 660w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;Hit &lt;strong&gt;Go&lt;/strong&gt; in the bottom and we are done here.&lt;/p&gt;
&lt;h2&gt;Create&lt;/h2&gt;
&lt;p&gt;Now the time has come to create a new project in &lt;strong&gt;Container Manager&lt;/strong&gt;. Launch it, open the &lt;strong&gt;Projects&lt;/strong&gt; view from the left, and hit the &lt;strong&gt;Create&lt;/strong&gt; button.&lt;/p&gt;
&lt;p&gt;Here we need to give our project a name, choose a previously created path for it, and provide a &lt;code&gt;docker-compose.yml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/hUtK0o-PfO-600.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;734&quot; height=&quot;469&quot; srcset=&quot;https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/hUtK0o-PfO-600.png 600w, https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/hUtK0o-PfO-734.png 734w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;And here is our &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;version: &#39;3.1&#39;

services:
  ghost:
    image: ghost:5.71.0 # Choose the version here: https://hub.docker.com/_/ghost/
    container_name: the_blog_ghost
    restart: unless-stopped
    ports:
      - 43332:2368 # Some random port mapped to a default Ghost port inside container
    volumes:
    # This is the path to our directory created at the beginning, mapped to a content directory inside the container
      - /volume1/docker/blog/content:/var/lib/ghost/content
    environment:
      database__client: mysql
      database__connection__host: 192.168.1.2 #This should be the local IP address of your NAS
      database__connection__user: blog_ghost #Database user we&#39;ve created previously
      database__connection__password: &#39;12345&#39; #The strongest password you can imagine 
      database__connection__database: my_blog #Database name we&#39;ve created previously
      url: https://blog.randomplace.online #Your future blog domain, you definitely already bought
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next step will allow us to create a web portal for our container on a port we mentioned in &lt;code&gt;docker-compose.yml&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/Chfe4ptY5Q-600.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;737&quot; height=&quot;578&quot; srcset=&quot;https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/Chfe4ptY5Q-600.png 600w, https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/Chfe4ptY5Q-737.png 737w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;We should accept this option and hit &lt;strong&gt;Next&lt;/strong&gt; and then &lt;strong&gt;Done&lt;/strong&gt; to start the container and navigate to a &lt;strong&gt;Web Station&lt;/strong&gt; to configure a Web Portal:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/_QNXv6_tk9-600.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;636&quot; height=&quot;403&quot; srcset=&quot;https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/_QNXv6_tk9-600.png 600w, https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/_QNXv6_tk9-636.png 636w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;Here we need to create a Name-based portal and use our domain as a Hostname. Also, we should force HTTPS (redirect all HTTP requests to a secure HTTPS connection) with the HSTS option:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/J73wXgZxu--597.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;597&quot; height=&quot;479&quot;&gt;&lt;/p&gt;
&lt;p&gt;The last thing we should do is generate a new SSL certificate to serve our blog through HTTPS.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Your domain name should already be pointed to your NAS&#39;s public IP address for the certificate generation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Go to &lt;strong&gt;Control Panel - Security&lt;/strong&gt; and choose the &lt;strong&gt;Certificate&lt;/strong&gt; tab. Here we can request a new free SSL certificate by clicking the Add button and choosing &amp;quot;Add new certificate&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/ULzFUXCo94-600.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;643&quot; height=&quot;545&quot; srcset=&quot;https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/ULzFUXCo94-600.png 600w, https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/ULzFUXCo94-643.png 643w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then &amp;quot;Get a certificate from Let&#39;s Encrypt&amp;quot; and fill in some fields:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/2ZSWvwIlKu-600.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;648&quot; height=&quot;546&quot; srcset=&quot;https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/2ZSWvwIlKu-600.png 600w, https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/2ZSWvwIlKu-648.png 648w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;You&#39;ll get an email notification on the address added here when there will be time to renew your free certificate.&lt;/p&gt;
&lt;p&gt;After certificate generation, we should open Settings on the same Certificates tab, find our domain service, and choose the newly generated certificate for it.&lt;/p&gt;
&lt;p&gt;That&#39;s it!&lt;/p&gt;
</description><pubDate>Tue, 31 Oct 2023 00:00:00 +0000</pubDate>
      <dc:creator>Yehor Vialov</dc:creator>
      <guid>https://www.yevi.org/notes/hosting-ghost-blog-on-synology-nas/</guid>
    </item>
    <item>
      <title>Using stepper motor to control amplifier volume knob with ESP8266 and ESPHome</title>
      <link>https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/</link><description>&lt;p&gt;My living room multimedia setup consists of numerous devices with varying degrees of stupidity. I’m chasing to improve it adding additional DIY hardware and functionality not always because of hate of TV remotes but also because I’m in love with IoT and soldering. First, I’ve added &lt;a href=&quot;https://www.yevi.org/projects/adding-wifi-power-control-to-an-integrated-amplifier&quot;&gt;WiFi power control to my Cambridge Audio amplifier&lt;/a&gt;, then I’ve implemented &lt;a href=&quot;https://www.yevi.org/projects/building-wifi-ir-remote-control-for-any-tv-with-esp8266-wemos-d1-mini-and-esphome&quot;&gt;WiFi-to-IR remote control to the TV&lt;/a&gt;. Then I decided to improve sound quality from TV and added a &lt;a href=&quot;https://wikipedia.org/wiki/Digital-to-analog_converter&quot;&gt;DAC&lt;/a&gt; between the TV and the amplifier. It is connected to the TV via &lt;a href=&quot;https://wikipedia.org/wiki/TOSLINK&quot;&gt;Toslink&lt;/a&gt; optical cable. And it broke my volume control because you can’t change the volume on a digital output port on a TV, you forced to rotate the knob on amplifier instead.&lt;/p&gt;
&lt;!-- truncate --&gt;
&lt;p&gt;I saw several solutions to this problem.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The easiest, but boring and expensive, is to purchase an improved model of my amplifier with IR remote control.&lt;/li&gt;
&lt;li&gt;Сheap and the one that looks right is to add a digital potentiometer to the amplifier input and control it with Wemos D1 mini I already have in my amp.&lt;/li&gt;
&lt;li&gt;Most inaccurate but fancy and the one you will use to impress your family and friends is to add a stepper motor that will rotate the knob for you.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I think you already guessed the way I choose, so let’s see how I achieved this.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/16gzyGq9kU-500.gif&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;281&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Components&lt;/h2&gt;
&lt;p&gt;As I already have Wemos D1 mini and a relay in my amplifier, you could see more components than you expect on photos, but, for simplicity, I’ll list only those components used to control the knob in this article.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;WEMOS (LOLIN) D1 mini WiFi board (&lt;a href=&quot;https://www.wemos.cc/en/latest/d1/d1_mini.html&quot;&gt;wemos.cc&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;AC-DC 220V to 5V Step-Down Mini Power Supply (&lt;a href=&quot;https://www.amazon.com/HLK-PM01-supply-module-intelligent-household/dp/B07G5GL4B8&quot;&gt;amazon&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;ULN2003 stepper motor driver&lt;/li&gt;
&lt;li&gt;5V 28BYJ-48 stepper motor&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Connections&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;5V DC power from power supply, 5V pin of Wemos D1 mini and +5V input of motor driver are connected together.&lt;/li&gt;
&lt;li&gt;Ground of the power supply, GND pin of Wemos D1 mini and -5V (ground) of motor driver are also connected together.&lt;/li&gt;
&lt;li&gt;IN1 of motor driver -&amp;gt; D5 on Wemos&lt;/li&gt;
&lt;li&gt;IN2 of motor driver -&amp;gt; D6 on Wemos&lt;/li&gt;
&lt;li&gt;IN3 of motor driver -&amp;gt; D7 on Wemos&lt;/li&gt;
&lt;li&gt;IN4 of motor driver -&amp;gt; D8 on Wemos&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;At the moment of writing ESPHome supported only two types of stepper motor drivers. See ESPHome &lt;a href=&quot;https://esphome.io/components/stepper/index.html&quot;&gt;documentation&lt;/a&gt; for more info.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Building&lt;/h2&gt;
&lt;p&gt;The AC-DC power supply was connected to the AC power input of the amplifier.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/ObPGvCvSQ6-600.png&quot; alt=&quot;Power connection&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;811&quot; srcset=&quot;https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/ObPGvCvSQ6-600.png 600w, https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/ObPGvCvSQ6-1000.png 1000w, https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/ObPGvCvSQ6-1200.png 1200w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now to the knob. The knob actually rotates an analog &lt;a href=&quot;https://wikipedia.org/wiki/Potentiometer&quot;&gt;potentiometer&lt;/a&gt; – a mechanical component with variable resistance. On my amplifier, all potentiometers have a hexagonal-shaped hole on the other side of the rotating element:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/8NO5vuVhd9-600.jpeg&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;888&quot; srcset=&quot;https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/8NO5vuVhd9-600.jpeg 600w, https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/8NO5vuVhd9-1000.jpeg 1000w, https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/8NO5vuVhd9-1200.jpeg 1200w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;This made the connection of the motor as easy as a couple of rasp strokes. I mean the shaft of my motor was slightly bigger than the hole on the potentiometer so I took a rasp and made it smaller. Then I used a couple of plastic racks with nuts and a bunch of hot glue to anchor the motor on amp housing.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/dtaC1LDU5H-600.jpeg&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;814&quot; srcset=&quot;https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/dtaC1LDU5H-600.jpeg 600w, https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/dtaC1LDU5H-1000.jpeg 1000w, https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/dtaC1LDU5H-1200.jpeg 1200w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/juBhT4C6F_-600.jpeg&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;739&quot; srcset=&quot;https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/juBhT4C6F_-600.jpeg 600w, https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/juBhT4C6F_-1000.jpeg 1000w, https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/juBhT4C6F_-1200.jpeg 1200w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/jm1PUOcy7X-600.jpeg&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;1194&quot; srcset=&quot;https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/jm1PUOcy7X-600.jpeg 600w, https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/jm1PUOcy7X-1000.jpeg 1000w, https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/jm1PUOcy7X-1200.jpeg 1200w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;I know it is not the best binding but it works. I don’t think this solution is permanent because I relay want to use digital potentiometers instead. I made this more for fun, but currently, this is the only way to remotely control the volume in my living room. Just to give you an opportunity to change your mind before you start a similar project (or, who knows, maybe to improve it) here is a list of issues it currently has:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is impossible to determine the current motor position because its position resets on each boot.&lt;/li&gt;
&lt;li&gt;It is easy to brake reduction gears in motor and even the amp when rotating the potentiometer further than it could.&lt;/li&gt;
&lt;li&gt;It is hard to rotate the knob manually because you need to rotate the motor as well.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are still here lats go to the software part.&lt;/p&gt;
&lt;h2&gt;ESPHome&lt;/h2&gt;
&lt;p&gt;ESPHome could rotate your stepper motors with &lt;a href=&quot;https://esphome.io/components/stepper/index.html&quot;&gt;Stepper Component&lt;/a&gt;. Here is my stepper config in ESPHome:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;stepper:
  - platform: uln2003
    id: volume_motor
    pin_a: D5
    pin_b: D6
    pin_c: D7
    pin_d: D8
    sleep_when_done: true
    max_speed: 250 steps/s
    acceleration: inf
    deceleration: inf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The idea is to change the volume on a given relative amount of motor steps. This will simplify the adjustment to find the right volume step for you. I declared a new service in &lt;code&gt;api&lt;/code&gt; section of my &lt;code&gt;amplifier.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;api:
  password: &amp;quot;*************&amp;quot;
  services:
    - service: set_volume
      variables:
        target: int
      then:
        - stepper.report_position:
            id: volume_motor
            position: 0
        - stepper.set_target:
            id: volume_motor
            target: !lambda &#39;return target;&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It would be exposed in Home Assistant as &lt;code&gt;esphome.amplifier_set_volume&lt;/code&gt; service and will take the only parameter &lt;code&gt;target&lt;/code&gt;. So in automation action increasing the volume in my case is looking like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/53zsNUTYLn-594.png&quot; alt=&quot;Automation action&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;594&quot; height=&quot;235&quot;&gt;&lt;/p&gt;
&lt;p&gt;Or in YAML:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;service: esphome.amplifier_set_volume
data:
  target: 50
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Decreasing the volume action is the same, but with negative &lt;code&gt;target&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;service: esphome.amplifier_set_volume
data:
  target: -50
&lt;/code&gt;&lt;/pre&gt;
</description><pubDate>Fri, 15 Jan 2021 00:00:00 +0000</pubDate>
      <dc:creator>Yehor Vialov</dc:creator>
      <guid>https://www.yevi.org/projects/using-stepper-motor-to-control-amplifier-volume-knob-with-esp8266-and-esphome/</guid>
    </item>
    <item>
      <title>IR code reader with ESP8266 Wemos D1 mini and ESPHome</title>
      <link>https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/</link><description>&lt;p&gt;Quick step-by-step guide on assembling an IR receiver for reading IR codes from your remotes based on Wemos D1 mini, Wemos IR shield, and using ESPHome.&lt;/p&gt;
&lt;!-- truncate --&gt;
&lt;h2&gt;Components used&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;The housing from Broadling RM Pro+&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.wemos.cc/en/latest/d1/d1_mini.html&quot;&gt;Wemos D1 mini&lt;/a&gt; development board&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.wemos.cc/en/latest/d1_mini_shield/ir.html&quot;&gt;Wemos IR shield&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;USB to micro-USB cable&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Assembling&lt;/h2&gt;
&lt;p&gt;First of all, we need to make our shield work with all transmitting LEDs and the pins we need. In this example, we will use &lt;code&gt;D3&lt;/code&gt; for sending and &lt;code&gt;D4&lt;/code&gt; for receiving.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/K1Wmp5ZH1i-600.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;919&quot; srcset=&quot;https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/K1Wmp5ZH1i-600.png 600w, https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/K1Wmp5ZH1i-1000.png 1000w, https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/K1Wmp5ZH1i-1200.png 1200w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/LyFa9KwhI2-600.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;719&quot; srcset=&quot;https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/LyFa9KwhI2-600.png 600w, https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/LyFa9KwhI2-1000.png 1000w, https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/LyFa9KwhI2-1200.png 1200w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/Bo7HntVsN7-600.png&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;717&quot; srcset=&quot;https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/Bo7HntVsN7-600.png 600w, https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/Bo7HntVsN7-1000.png 1000w, https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/Bo7HntVsN7-1200.png 1200w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now lets put the board into the housing with cable connected and glue it all:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/8vfmi4bt5d-600.jpeg&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;900&quot; srcset=&quot;https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/8vfmi4bt5d-600.jpeg 600w, https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/8vfmi4bt5d-1000.jpeg 1000w, https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/8vfmi4bt5d-1200.jpeg 1200w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then put the shield on top:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/vlaPgEfqdY-600.jpeg&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;900&quot; srcset=&quot;https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/vlaPgEfqdY-600.jpeg 600w, https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/vlaPgEfqdY-1000.jpeg 1000w, https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/vlaPgEfqdY-1200.jpeg 1200w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;p&gt;Finally, close the housing:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/mmAuP-oG7l-600.jpeg&quot; alt=&quot;&quot; decoding=&quot;async&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;689&quot; srcset=&quot;https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/mmAuP-oG7l-600.jpeg 600w, https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/mmAuP-oG7l-1000.jpeg 1000w, https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/mmAuP-oG7l-1200.jpeg 1200w&quot; sizes=&quot;100vw&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Using&lt;/h2&gt;
&lt;p&gt;In ESPHome let’s create a config:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;esphome:
  name: ir_receiver
  platform: ESP8266
  board: d1_mini

wifi:
  ssid: &amp;quot;WiFi&amp;quot;
  password: &amp;quot;************&amp;quot;

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: &amp;quot;IR Receiver&amp;quot;
    password: &amp;quot;************&amp;quot;

captive_portal:

# Enable logging
logger:
  level: DEBUG

# Enable Home Assistant API
api:
  password: &amp;quot;************&amp;quot;

ota:
  password: &amp;quot;************&amp;quot;

remote_receiver:
  pin:
    number: D4
    inverted: true
  dump:
    - samsung
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see it is set up to dump only Samsung-specific codes. You can read more about reading other codes on &lt;a href=&quot;https://esphome.io/components/remote_receiver.html&quot;&gt;esphome.io&lt;/a&gt;. Also pay attention on &lt;code&gt;logger&lt;/code&gt; section. It is set to &lt;code&gt;DEBUG&lt;/code&gt; level.&lt;/p&gt;
&lt;p&gt;Compile, flash, open logs and start firing into our device with your IR remote. You’ll see something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[17:34:48][D][remote.samsung:055]: Received Samsung: data=0xE0E007F8
[17:34:51][D][remote.samsung:055]: Received Samsung: data=0xE0E020DF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now use this data with the ESPHome &lt;a href=&quot;https://esphome.io/components/remote_transmitter.html&quot;&gt;Remote Transmitter&lt;/a&gt; component to build, for example, something like &lt;a href=&quot;https://www.yevi.org/projects/building-wifi-ir-remote-control-for-any-tv-with-esp8266-wemos-d1-mini-and-esphome&quot;&gt;this&lt;/a&gt;.&lt;/p&gt;
</description><pubDate>Fri, 15 Jan 2021 00:00:00 +0000</pubDate>
      <dc:creator>Yehor Vialov</dc:creator>
      <guid>https://www.yevi.org/notes/ir-code-reader-with-esp8266-wemos-d1-mini-and-esphome/</guid>
    </item>
  </channel>
</rss>