<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>gsvd</title>
		<link>https://gsvd.dev</link>
		<description>Articles from gsvd.dev — Backend Software Developer</description>
		<language>en-us</language>
		<atom:link href="https://gsvd.dev/feed.xml" rel="self" type="application/rss+xml"></atom:link>
		<item>
			<title>My Prosody setup with Docker Compose</title>
			<link>https://gsvd.dev/blog/my-prosody-setup-with-docker-compose</link>
			<guid>https://gsvd.dev/blog/my-prosody-setup-with-docker-compose</guid>
			<pubDate>Sat, 21 Mar 2026 18:12:00 -0400</pubDate>
			<description>I spent hours finding the best way to integrate Prosody into my Docker Compose stack. Here is what I found out.</description>
			<content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;After all these years spent secretly refusing to welcome Docker into my self-hosting journey,&#xA;I finally switched to a Docker Compose Stack - way of managing most of my applications.&#xA;Probably because I was bored, Debian is too stable.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Also, I love XMPP. I use this protocol every day to chat with my friends.&#xA;This is why I have been hosting a &lt;a href=&#34;https://prosody.im/&#34;&gt;Prosody&lt;/a&gt; server for a while now.&#xA;And while we are making the presentations, let me not introduce, because you already know him: &lt;a href=&#34;https://gajim.org/&#34;&gt;Gajim&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This article will guide you through the process of deploying a Prosody XMPP server using Docker Compose.&#xA;Thanks to it, you will be able to chat securely with all of your friends using a mature communication protocol.&#xA;I will not cover all the DNS configuration, but if you need guidance for it and you read French, I&amp;rsquo;d recommend &lt;a href=&#34;https://toutetrien.lithio.fr/article/installer-son-serveur-xmpp-avec-prosody/&#34;&gt;Installer son serveur XMPP avec Prosody&lt;/a&gt; from a friend of mine. I am still covering the TLS part (using Let&amp;rsquo;s Encrypt) so bear with me, we are starting.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;The Compose directory&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;We will be using the official &lt;a href=&#34;https://hub.docker.com/r/prosodyim/prosody/&#34;&gt;Prosody Docker image&lt;/a&gt; and start from the &lt;code&gt;docker-compose.yml&lt;/code&gt; example available in the &lt;a href=&#34;https://hub.docker.com/r/prosodyim/prosody/&#34;&gt;official documentation&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Remember, you can also consult my Docker Stack at any time directly on my VCS &lt;a href=&#34;https://vcs.gsvd.dev/gsvd/stacks&#34;&gt;vcs.gsvd.dev/gsvd/stacks&lt;/a&gt; (in case the example below may be outdated).&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;my-docker-stack/&#xA;├── some-application/&#xA;└── prosody/&#xA;    ├── hooks/&#xA;    │   └── deploy.sh&#xA;    ├── config/&#xA;    │   ├── conf.d/&#xA;    │   │   └── example.com.cfg.lua&#xA;    │   └── prosody.cfg.lua&#xA;    └── compose.yml&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2&gt;Compose file&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;We&amp;rsquo;ll need TLS certificates for our Virtual Host. You can either choose to manage them on your host machine or use the &lt;a href=&#34;https://hub.docker.com/r/certbot/certbot&#34;&gt;certbot Docker image&lt;/a&gt;. I&amp;rsquo;ll be covering the second option, but both are nearly identical: the most important thing is the deploy hook so you can just extract it.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;compose.yml&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;You will need to accept incoming connections on port 80 (host machine) for certbot&amp;rsquo;s HTTP-01 challenge.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;services:&#xA;  prosody:&#xA;    image: prosodyim/prosody:13.0&#xA;    pull_policy: always&#xA;    restart: unless-stopped&#xA;    ports:&#xA;      - 5222:5222 # C2S STARTTLS&#xA;      - 5269:5269 # S2S STARTTLS&#xA;      - 5281:5281 # HTTPS&#xA;      - 5000:5000 # File transfer proxy&#xA;    volumes:&#xA;      - ./config:/etc/prosody&#xA;      - certs:/etc/prosody/certs&#xA;      - data:/var/lib/prosody&#xA;    depends_on:&#xA;      certbot:&#xA;        condition: service_healthy&#xA;&#xA;  certbot:&#xA;    image: certbot/certbot&#xA;    restart: unless-stopped&#xA;    volumes:&#xA;      - certs:/etc/letsencrypt&#xA;      - ./hooks/deploy.sh:/etc/letsencrypt/renewal-hooks/deploy/prosody.sh:ro&#xA;    ports:&#xA;      - &amp;quot;80:80&amp;quot;&#xA;    entrypoint:&#xA;      - /bin/sh&#xA;      - -c&#xA;      - |&#xA;        set -e&#xA;        certbot certonly --standalone \&#xA;          --agree-tos --non-interactive --keep-until-expiring \&#xA;          --email me@example.com \&#xA;          -d example.com \&#xA;          -d p.example.com \&#xA;          -d u.example.com \&#xA;          -d c.example.com&#xA;        echo &#39;0 3 * * * certbot renew --quiet&#39; | crontab -&#xA;        crond -f -d 8&#xA;    healthcheck:&#xA;      test: [&amp;quot;CMD&amp;quot;, &amp;quot;test&amp;quot;, &amp;quot;-f&amp;quot;, &amp;quot;/etc/letsencrypt/live/example.com/fullchain.pem&amp;quot;]&#xA;      interval: 10s&#xA;      timeout: 5s&#xA;      retries: 30&#xA;      start_period: 5s&#xA;&#xA;volumes:&#xA;  certs:&#xA;  data:&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h3&gt;deploy.sh&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;You can see that we use a Docker volume named &lt;code&gt;certs&lt;/code&gt; for both certbot and Prosody.&#xA;But since they don&amp;rsquo;t follow the same path pattern (plus Let&amp;rsquo;s Encrypt symlinks), we have to make some copy-pasta to make it work.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;As I was saying, this deploy hook is also valid on your host machine (you can maybe just adapt the paths a little).&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;#!/bin/sh&#xA;&#xA;mkdir -p /etc/letsencrypt/example.com&#xA;cp /etc/letsencrypt/live/example.com/fullchain.pem /etc/letsencrypt/example.com/&#xA;cp /etc/letsencrypt/live/example.com/privkey.pem /etc/letsencrypt/example.com/&#xA;chmod 644 /etc/letsencrypt/example.com/fullchain.pem&#xA;chmod 640 /etc/letsencrypt/example.com/privkey.pem&#xA;chown 100:102 /etc/letsencrypt/example.com/fullchain.pem&#xA;chown 100:102 /etc/letsencrypt/example.com/privkey.pem&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Let&amp;rsquo;s take some perspective on this hook:&lt;/p&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;You are probably wondering why we are doing a &lt;code&gt;chown 100:102&lt;/code&gt;, because it&amp;rsquo;s the UID and GID used by the Prosody image. You can double check at any time using &lt;code&gt;docker run --rm --entrypoint id prosodyim/prosody:13.0 prosody&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;The script will be mounted into &lt;code&gt;/etc/letsencrypt/renewal-hooks/deploy&lt;/code&gt; and will automatically be picked up by certbot after each generation and renewal.&lt;/li&gt;&#xA;&lt;li&gt;Because it&amp;rsquo;s always better to visualize things, here is a quick diagram to explain how and why this hook exists: &lt;a href=&#34;/static/images/3/certs_hook_explained.png&#34;&gt;click to open&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;h3&gt;Common troubleshooting&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;If you already have a web server listening on port &lt;code&gt;80&lt;/code&gt; you can change the one used by certbot to something else, for example &lt;code&gt;61248:80&lt;/code&gt;, and redirect using a reverse proxy.&#xA;This Nginx example will redirect the HTTP-01 challenge to the right port used by certbot in Docker.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;server {&#xA;    listen 80;&#xA;    server_name example.com p.example.com u.example.com c.example.com;&#xA;&#xA;    location /.well-known/acme-challenge/ {&#xA;        proxy_pass http://127.0.0.1:61248;&#xA;    }&#xA;&#xA;    # You can still serve anything you want here.&#xA;    # For example, I host my own OMEMO public keys here.&#xA;    # It&#39;s useful if you have paranoid friends, for this example 404 will be enough.&#xA;    location / {&#xA;        return 404;&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2&gt;Prosody configuration&lt;/h2&gt;&#xA;&#xA;&lt;h3&gt;prosody.cfg.lua&lt;/h3&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;modules_enabled = {&#xA;    &amp;quot;adhoc&amp;quot;, &amp;quot;admin_adhoc&amp;quot;, &amp;quot;admin_shell&amp;quot;, &amp;quot;announce&amp;quot;,&#xA;    &amp;quot;blocklist&amp;quot;, &amp;quot;bookmarks&amp;quot;, &amp;quot;bosh&amp;quot;, &amp;quot;carbons&amp;quot;,&#xA;    &amp;quot;cloud_notify&amp;quot;, &amp;quot;csi_simple&amp;quot;, &amp;quot;dialback&amp;quot;, &amp;quot;disco&amp;quot;,&#xA;    &amp;quot;mam&amp;quot;, &amp;quot;mimicking&amp;quot;, &amp;quot;offline&amp;quot;, &amp;quot;pep&amp;quot;,&#xA;    &amp;quot;ping&amp;quot;, &amp;quot;private&amp;quot;, &amp;quot;register&amp;quot;, &amp;quot;roster&amp;quot;,&#xA;    &amp;quot;s2s_bidi&amp;quot;, &amp;quot;saslauth&amp;quot;, &amp;quot;smacks&amp;quot;, &amp;quot;time&amp;quot;,&#xA;    &amp;quot;tls&amp;quot;, &amp;quot;tombstones&amp;quot;, &amp;quot;uptime&amp;quot;, &amp;quot;vcard_legacy&amp;quot;,&#xA;    &amp;quot;version&amp;quot;, &amp;quot;watchregistrations&amp;quot;, &amp;quot;websocket&amp;quot;, &amp;quot;welcome&amp;quot;,&#xA;}&#xA;&#xA;allow_registration = false&#xA;authentication = &amp;quot;internal_hashed&amp;quot;&#xA;&#xA;c2s_require_encryption = true&#xA;s2s_require_encryption = true&#xA;s2s_secure_auth = true&#xA;&#xA;pep_max_items = 12800&#xA;archive_expires_after = &amp;quot;3w&amp;quot;&#xA;&#xA;log = {&#xA;    &amp;quot;*console&amp;quot;;&#xA;}&#xA;&#xA;Include &amp;quot;conf.d/*.cfg.lua&amp;quot;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h3&gt;example.com.cfg.lua&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;You may want to take a few minutes to check the security options I provide, I highly recommend &lt;a href=&#34;https://ssl-config.mozilla.org/&#34;&gt;ssl-config.mozilla.org&lt;/a&gt;.&#xA;We are using sqlite, but you can change it to postgres if you need. It&amp;rsquo;ll require some adjustments both to the Prosody configuration and compose file (not covering it here).&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;VirtualHost &amp;quot;example.com&amp;quot;&#xA;ssl = {&#xA;    key = &amp;quot;/etc/prosody/certs/example.com/privkey.pem&amp;quot;,&#xA;    certificate = &amp;quot;/etc/prosody/certs/example.com/fullchain.pem&amp;quot;,&#xA;    ciphers = &amp;quot;ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305&amp;quot;,&#xA;    protocol = &amp;quot;tlsv1_2+&amp;quot;,&#xA;    curve = &amp;quot;X25519:prime256v1:secp384r1&amp;quot;,&#xA;    options = {&#xA;        &amp;quot;cipher_server_preference&amp;quot;,&#xA;        &amp;quot;no_compression&amp;quot;,&#xA;        &amp;quot;no_ticket&amp;quot;,&#xA;        &amp;quot;single_ecdh_use&amp;quot;&#xA;    }&#xA;}&#xA;&#xA;authentication = &amp;quot;internal_hashed&amp;quot;&#xA;storage = &amp;quot;sql&amp;quot;&#xA;sql = { driver = &amp;quot;SQLite3&amp;quot;, database = &amp;quot;prosody&amp;quot; }&#xA;&#xA;Component &amp;quot;c.example.com&amp;quot; &amp;quot;muc&amp;quot;&#xA;    name = &amp;quot;Rooms&amp;quot;&#xA;    restrict_room_creation = &amp;quot;local&amp;quot;&#xA;    muc_max_rooms = 64&#xA;    muc_room_default_public = false&#xA;&#xA;Component &amp;quot;u.example.com&amp;quot; &amp;quot;http_file_share&amp;quot;&#xA;    name = &amp;quot;Files&amp;quot;&#xA;    http_file_share_size_limit = 512 * 1024 * 1024       -- 512 MB per file&#xA;    http_file_share_daily_quota = 4096 * 1024 * 1024     -- 4 GB per user per day&#xA;    http_file_share_expire_after = 60 * 60 * 24 * 14     -- 14 days expire&#xA;&#xA;Component &amp;quot;p.example.com&amp;quot; &amp;quot;proxy65&amp;quot;&#xA;    name = &amp;quot;Proxy&amp;quot;&#xA;    proxy65_address = &amp;quot;p.example.com&amp;quot;&#xA;    proxy65_acl = { &amp;quot;example.com&amp;quot; }&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2&gt;Conclusion&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;If you&amp;rsquo;ve done everything right, from DNS to TLS: you can just &lt;code&gt;docker compose up -d&lt;/code&gt;. This will generate TLS certificates for your domain and boot your Prosody server.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;To start chatting with your friends, create a user &lt;code&gt;docker compose exec prosody prosodyctl adduser my-user@example.com&lt;/code&gt; and you&amp;rsquo;re good to go.&#xA;You can contact me on XMPP &lt;code&gt;gsvd@gameover.sh&lt;/code&gt;, send me a message and tell me if this quick guide helped you!&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Also, keep in mind that this article has been written without any help from AI. This is based on my own research and findings, and it describes my own approach to Compose files.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;Takeaways&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Of course this is not a &amp;ldquo;production-ready&amp;rdquo; tutorial. Keep in mind a few things before that:&lt;/p&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Backup: everything that needs to be backed up lives in the &lt;code&gt;data&lt;/code&gt; volume. Don&amp;rsquo;t forget to include it in your existing backup flow.&lt;/li&gt;&#xA;&lt;li&gt;DNS configuration, we didn&amp;rsquo;t cover it here but this step is straightforward (don&amp;rsquo;t forget &lt;a href=&#34;https://toutetrien.lithio.fr/article/installer-son-serveur-xmpp-avec-prosody/&#34;&gt;this link&lt;/a&gt; or the &lt;a href=&#34;https://prosody.im/doc/dns&#34;&gt;official documentation&lt;/a&gt;).&lt;/li&gt;&#xA;&lt;li&gt;Tweak the &lt;code&gt;http_file_share_*&lt;/code&gt; settings, I have a lot of storage so I&amp;rsquo;m quite generous.&lt;/li&gt;&#xA;&lt;li&gt;Once your server is live, join the Prosody IM Chatroom &lt;code&gt;prosody@conference.prosody.im&lt;/code&gt; to say hello!&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;{p,u,c}.example.com&lt;/code&gt; subdomains are just my personal naming scheme, I&amp;rsquo;m pretty sure the convention is &lt;code&gt;{proxy,upload,conference}.example.com&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;</content:encoded>
		</item>
		<item>
			<title>Host your own password manager with Vaultwarden and Nginx</title>
			<link>https://gsvd.dev/blog/host-your-own-password-manager-with-vaultwarden-and-nginx</link>
			<guid>https://gsvd.dev/blog/host-your-own-password-manager-with-vaultwarden-and-nginx</guid>
			<pubDate>Thu, 30 Jan 2025 22:47:00 -0500</pubDate>
			<description>Vaultwarden comes with all applications and browser extensions you need, it&#39;s simple to install, so why not just using it?</description>
			<content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;A few years ago, I used Nextcloud a lot, combined with its &lt;a href=&#34;https://apps.nextcloud.com/apps/passwords&#34;&gt;Passwords&lt;/a&gt; app. I can&amp;rsquo;t say it was a bad user experience, but it wasn&amp;rsquo;t a good one either. Later, I dropped Nextcloud because I realized that I didn&amp;rsquo;t need it that much, so I no longer had a password manager (yes, I know, it&amp;rsquo;s bad, but who cares?).&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Now that I&amp;rsquo;m in a new phase of trying and hosting things, I wanted to give &lt;a href=&#34;https://github.com/dani-garcia/vaultwarden&#34;&gt;Vaultwarden&lt;/a&gt; a try. And you know what? I loved it so much that I started writing this little blog post about it, because it deserves it, and I really want you to give it a chance.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I must admit that I&amp;rsquo;m not a big fan of containers. While I can&amp;rsquo;t say I like Podman, I don&amp;rsquo;t dislike it as much as Docker. So when I read Vaultwarden&amp;rsquo;s installation documentation and saw &amp;ldquo;You can replace Docker with Podman if you prefer to use Podman.&amp;rdquo; it really appealed to me.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Well, enough talking! Let&amp;rsquo;s get into what we like&amp;hellip;&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;Requirements&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Oh yes, big announcement: I&amp;rsquo;m no longer into Raspberry Pi since my last post. So here&amp;rsquo;s the setup I have and will assume you have for the technical parts. The versions don&amp;rsquo;t have to be exactly the same, we don&amp;rsquo;t really care, and anyway, if it causes an issue, you&amp;rsquo;ll find out soon enough.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Note that at the time of writing, the current version of Vaultwarden is &lt;code&gt;1.33.0&lt;/code&gt;.&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Debian 12&lt;/li&gt;&#xA;&lt;li&gt;Nginx 1.22.1 (we&amp;rsquo;ll be using a reverse proxy)&lt;/li&gt;&#xA;&lt;li&gt;Certbot 2.1.0 (because in HTTPS we trust — though you probably shouldn&amp;rsquo;t). While I won’t explain this step, I still recommend setting it up on your own.&lt;/li&gt;&#xA;&lt;li&gt;Podman 4.3.1 (don&amp;rsquo;t forget that you can easily replace it by Docker)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h2&gt;Run Vaultwarden using Podman&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;We will be using the &lt;code&gt;/vw-data&lt;/code&gt; directory, so make sure to create it first and ensure you have the proper user permissions on it (root or whatever works for you). Feel free to adapt the command below.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;podman run --detach \&#xA;    --name vaultwarden \&#xA;    --env DOMAIN=&amp;quot;https://example.com&amp;quot; \&#xA;    --env ADMIN_TOKEN=&amp;quot;SOME_RANDOM_STRING&amp;quot; \&#xA;    --volume /vw-data/:/data/ \&#xA;    --restart unless-stopped \&#xA;    --publish 127.0.0.1:8080:80 \&#xA;    docker.io/vaultwarden/server:latest&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Don&amp;rsquo;t worry, thanks to the &lt;code&gt;--restart unless-stopped&lt;/code&gt; parameter, it will always be up, even after a reboot.&lt;/li&gt;&#xA;&lt;li&gt;The &lt;code&gt;ADMIN_TOKEN&lt;/code&gt; will allow you to access the admin backend at &lt;code&gt;https://example.com/admin&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h2&gt;Nginx reverse proxy&lt;/h2&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;This is an HTTP configuration. If you plan to expose it to the web, you should really add an HTTPS configuration.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;server {&#xA;    listen 80;&#xA;    listen [::]:80;&#xA;&#xA;    server_name example.com;&#xA;&#xA;    location / {&#xA;        proxy_pass http://127.0.0.1:8080;&#xA;        proxy_set_header Host $host;&#xA;        proxy_set_header X-Real-IP $remote_addr;&#xA;        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&#xA;        proxy_set_header X-Forwarded-Proto $scheme;&#xA;        proxy_redirect off;&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Add this configuration in your &lt;code&gt;/etc/nginx/sites-available&lt;/code&gt; directory then enable it:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;sudo nano /etc/nginx/sites-available/example.com&#xA;sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled&#xA;sudo nginx -t&#xA;sudo systemctl reload nginx&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;From here you can try to access to &lt;code&gt;http://example.com/&lt;/code&gt; — it should work.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Don’t forget to check out &lt;code&gt;http://example.com/admin&lt;/code&gt; as well, using your &lt;code&gt;ADMIN_TOKEN&lt;/code&gt; from the previous step.&lt;/p&gt;&#xA;</content:encoded>
		</item>
		<item>
			<title>Create a seedbox on Raspberry Pi using the latest version of qbittorrent</title>
			<link>https://gsvd.dev/blog/create-a-seedbox-on-raspberry-pi-using-the-latest-version-of-qbittorrent</link>
			<guid>https://gsvd.dev/blog/create-a-seedbox-on-raspberry-pi-using-the-latest-version-of-qbittorrent</guid>
			<pubDate>Mon, 06 Nov 2023 21:34:00 -0500</pubDate>
			<description>qBittorrent has consistently stood out for its comprehensive features. However, as of the time of writing, the only version of qbittorrent-nox available for Raspbian is somewhat outdated (4.1.5).</description>
			<content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;A seedbox is a dedicated, high-speed server for downloading and uploading files.&#xA;Most commonly used in the context of torrenting, a seedbox provides many benefits over using a personal computer for the same tasks.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;Throughout my experiences with various torrent clients, qBittorrent has consistently stood out for its comprehensive features.&#xA;However, as of the time of writing, the only version of qbittorrent-nox available for Raspbian is somewhat outdated (4.1.5).&#xA;Compiling from source can be a daunting task, so thankfully, &lt;a href=&#34;https://github.com/userdocs/qbittorrent-nox-static&#34;&gt;qbittorrent-nox-static&lt;/a&gt; provides a convenient solution.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;User creation&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Let&amp;rsquo;s begin by creating a user specifically for running qBittorrent securely. This step is straightforward:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;adduser qbittorrent&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2&gt;Download binaries&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The following steps can be found in the &lt;a href=&#34;https://github.com/userdocs/qbittorrent-nox-static&#34;&gt;qbittorrent-nox-static&lt;/a&gt; documentation.&#xA;Ensure you execute these commands while logged in as the newly created user.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;mkdir -p ~/bin &amp;amp;&amp;amp; source ~/.profile&#xA;wget -qO ~/bin/qbittorrent-nox https://github.com/userdocs/qbittorrent-nox-static/releases/latest/download/armv7-qbittorrent-nox&#xA;chmod 700 ~/bin/qbittorrent-nox&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Next, launch qBittorrent to initialize the configuration files, then terminate the process.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;~/bin/qbittorrent-nox&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2&gt;qBittorrent port configuration&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Edit the configuration file by running:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;nano ~/.config/qBittorrent/qBittorrent.conf&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;You’ll find many settings here, but we’ll focus on the essential ones. Locate the following line and set your desired port:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;Session\Port=65530&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2&gt;Service creation&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Execute the next commands as the root user or with sudo privileges.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;nano /etc/systemd/system/qbittorrent.service&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Copy, paste, and modify the content below as needed:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;[Unit]&#xA;Description=qBittorrent-nox service&#xA;Documentation=man:qbittorrent-nox(1)&#xA;Wants=network-online.target&#xA;After=network-online.target nss-lookup.target&#xA;&#xA;[Service]&#xA;Type=exec&#xA;User=qbittorrent&#xA;ExecStart=/home/qbittorrent/bin/qbittorrent-nox&#xA;&#xA;[Install]&#xA;WantedBy=multi-user.target&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Then, remember to enable, start, and check the service&amp;rsquo;s status:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;systemctl enable qbittorrent&#xA;systemctl start qbittorrent&#xA;systemctl status qbittorrent&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2&gt;Firewall (using UFW)&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Allow the necessary ports through the firewall, when the Nginx reverse proxy is configured, you can remove port 8080 from your authorisations:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;ufw allow 8080/tcp comment &#39;qBittorrent WebUI&#39;&#xA;ufw allow 65530/tcp comment &#39;libTorrent&#39;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2&gt;Router port redirection&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Here&amp;rsquo;s a brief reminder: ensure that your server is configured with a static IP and that port forwarding is functioning correctly.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;Access to web interface&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The web interface should now be accessible. Navigate to &lt;code&gt;http://&amp;lt;your-ip&amp;gt;:8080&lt;/code&gt; in your web browser. The default credentials are &lt;code&gt;admin/adminadmin&lt;/code&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Immediately change the default login details:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&#34;/static/images/1/qbittorrent-user-access.png&#34; alt=&#34;qbittorrent-user-access&#34; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;Nginx configuration&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;We will briefly discuss setting up a virtual host (vhost). Detailed guides on installing Nginx, generating SSL certificates, and DNS configuration are available online, so we won’t cover those here.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;However, ensure you&amp;rsquo;ve configured HTTPS before exposing your web interface to the internet.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;nano /etc/nginx/sites-available/qbittorrent.conf&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Copy, paste, and modify the content below as needed:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;server {&#xA;  listen 80 http2;&#xA;  listen [::]:80 http2;&#xA;&#xA;  server_name example.com;&#xA;&#xA;  location / {&#xA;    proxy_pass              http://127.0.0.1:8080;&#xA;    proxy_set_header        X-Forwarded-Host        $server_name:$server_port;&#xA;    proxy_set_header        X-Forwarded-Host        $http_host;&#xA;    proxy_set_header        X-Forwarded-For         $remote_addr;&#xA;  }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Don&amp;rsquo;t forget to enable this Nginx configuration:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;ln -s /etc/nginx/sites-available/qbittorrent.conf /etc/nginx/sites-enabled&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2&gt;Going further&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Here are some additional considerations for future exploration:&lt;/p&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Mounting external storage to save downloaded files. Configure qBittorrent to save to specific directories and move files upon completion.&lt;/li&gt;&#xA;&lt;li&gt;Install &lt;a href=&#34;https://jellyfin.org/&#34;&gt;Jellyfin&lt;/a&gt; to create a personal media center.&lt;/li&gt;&#xA;&lt;li&gt;Setting up a VPN, like &lt;a href=&#34;https://www.pivpn.io/&#34;&gt;PiVPN&lt;/a&gt;, to access your seedbox from anywhere without using Nginx.&lt;/li&gt;&#xA;&lt;li&gt;Securing your server with &lt;a href=&#34;https://github.com/fail2ban/fail2ban&#34;&gt;fail2ban&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Implementing an &lt;a href=&#34;https://github.com/qbittorrent/qBittorrent/wiki/List-of-known-alternate-WebUIs&#34;&gt;alternative WebUI&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Torrenting more safely by setting up proxy usage.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;h2&gt;Conclusion&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Setting up a seedbox on a Raspberry Pi with the latest version of qBittorrent can seem like a technical venture,&#xA;but it offers a great way to enhance your torrenting experience with added control,&#xA;efficiency, and privacy. This guide aimed to provide you with a clear path to getting your own seedbox up and running,&#xA;from installing qBittorrent to configuring your system for remote access.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;With your new setup, not only can you manage your downloads more effectively,&#xA;but you also have a foundation to build upon. Whether you plan to expand your server’s capabilities,&#xA;explore media streaming options or further secure your system, the steps provided here give you a solid start.&#xA;Remember, the key to a successful seedbox is not just in its creation but also in its maintenance and security.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I hope this tutorial has been helpful, and I encourage you to share your experiences, ask questions, and provide feedback.&#xA;The DIY spirit of the Raspberry Pi community thrives on shared knowledge and collaboration,&#xA;so don&amp;rsquo;t hesitate to contribute your own findings.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Happy torrenting!&lt;/p&gt;&#xA;</content:encoded>
		</item>
	</channel>
</rss>