<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Self-Hosting on Aquasp's blog</title><link>https://aquasp.blog/tag/self-hosting/</link><description>Recent content in Self-Hosting on Aquasp's blog</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Fri, 15 May 2026 15:53:11 +0000</lastBuildDate><atom:link href="https://aquasp.blog/tag/self-hosting/index.xml" rel="self" type="application/rss+xml"/><item><title>Fixing Blank/Grey Screen on Album Pages in Finamp (Jellyfin Music Player)</title><link>https://aquasp.blog/fixing-blank-grey-screen-on-album-pages-in-finamp-jellyfin-music-player/</link><pubDate>Mon, 15 Dec 2025 15:02:30 +0000</pubDate><guid>https://aquasp.blog/fixing-blank-grey-screen-on-album-pages-in-finamp-jellyfin-music-player/</guid><description>&lt;p&gt;If you&amp;rsquo;re using &lt;strong&gt;Finamp&lt;/strong&gt; to stream or download music from your &lt;strong&gt;Jellyfin&lt;/strong&gt; server and encountering a blank (or grey) screen when opening an album page—where no tracks load and you may need to force-close the app—this is a common issue, especially after upgrading Jellyfin to version 10.11.x.&lt;/p&gt;&#10;&lt;p&gt;This problem typically occurs due to changes in how Jellyfin handles music metadata in newer versions, causing Finamp to fail loading album tracks properly.&lt;/p&gt;&#10;&lt;h2 id="the-fix-refresh-metadata-in-jellyfin-search-for-missing-metadata"&gt;The Fix: Refresh Metadata in Jellyfin (Search for Missing Metadata)&#10;&lt;/h2&gt;&lt;p&gt;The most reliable workaround is to force Jellyfin to refresh your music library metadata:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Log in to your Jellyfin web dashboard (admin account recommended).&lt;/li&gt;&#10;&lt;li&gt;Go to &lt;strong&gt;Dashboard&lt;/strong&gt; &amp;gt; &lt;strong&gt;Libraries&lt;/strong&gt;.&lt;/li&gt;&#10;&lt;li&gt;Select your &lt;strong&gt;Music&lt;/strong&gt; library.&lt;/li&gt;&#10;&lt;li&gt;Click the three dots (&amp;hellip;) menu next to the library name.&lt;/li&gt;&#10;&lt;li&gt;Choose &lt;strong&gt;Refresh Metadata&lt;/strong&gt;.&lt;/li&gt;&#10;&lt;li&gt;In the refresh options:&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Check &lt;strong&gt;Search for missing metadata&lt;/strong&gt;.&lt;/li&gt;&#10;&lt;li&gt;Optionally enable other options like &amp;ldquo;Replace existing images&amp;rdquo; or &amp;ldquo;Replace all metadata&amp;rdquo; if needed.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;ol start="7"&gt;&#10;&lt;li&gt;Click &lt;strong&gt;Refresh&lt;/strong&gt; (or OK) to start the scan.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This process can take several minutes depending on your library size. Once done, restart Finamp and try opening albums again—they should now load tracks normally.&lt;/p&gt;&#10;&lt;h2 id="additional-notes-on-jellyfin-1011x"&gt;Additional Notes on Jellyfin 10.11.x&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;The Finamp team and users have reported &lt;strong&gt;performance regressions&lt;/strong&gt; in Jellyfin 10.11.x, particularly with music libraries (slower loading, higher resource usage).&lt;/li&gt;&#10;&lt;li&gt;If possible, consider staying on or downgrading to Jellyfin 10.10.x for better performance until fixes are fully rolled out in later 10.11 patches.&lt;/li&gt;&#10;&lt;li&gt;For me. I can confirm that even on the latest 10.11.x, the metadata refresh resolves the blank album issue, and overall functionality works well afterward.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="credits"&gt;Credits&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;GitHub Issue: &lt;a class="link" href="https://github.com/jmshrv/finamp/issues/1412?referrer=grok.com&amp;amp;ref=aquasp.blog" target="_blank" rel="noopener"&#10; &gt;https://github.com/jmshrv/finamp/issues/1412&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;If the issue persists after the scan, check for Finamp updates or report it on the Finamp GitHub repository. Happy listening! :)&lt;/p&gt;&#10;</description></item><item><title>How to easily export and import docker volumes</title><link>https://aquasp.blog/how-to-easily-export-and-import-docker-volumes/</link><pubDate>Tue, 09 Dec 2025 01:22:26 +0000</pubDate><guid>https://aquasp.blog/how-to-easily-export-and-import-docker-volumes/</guid><description>&lt;p&gt;If you’re like me, you prefer to run &lt;strong&gt;everything&lt;/strong&gt; in Docker containers. They’re fast, isolated, and perfect for running multiple apps on one VPS.&lt;/p&gt;&#10;&lt;p&gt;But what happens when you want to:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Move a container to a new server?&lt;/li&gt;&#10;&lt;li&gt;Backup a database volume (NextCloud, PhotoPrism, Vaultwarden, etc.)?&lt;/li&gt;&#10;&lt;li&gt;Restore data after a crash?&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Docker doesn’t include a built-in “export volume” button — but there’s a &lt;strong&gt;super simple and reliable trick&lt;/strong&gt; using a temporary Ubuntu container.&lt;/p&gt;&#10;&lt;p&gt;Let’s go!&lt;/p&gt;&#10;&lt;h2 id="step-1-list-your-volumes"&gt;Step 1: List Your Volumes&#10;&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker volume ls&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Example output:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;DRIVER VOLUME NAME&#10;local nextcloud_data&#10;local photoprism_storage&#10;local vaultwarden_data&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pick the one you want to export (e.g., nextcloud_data).&lt;/p&gt;&#10;&lt;h2 id="step-2-export-a-volume--backuptargz"&gt;Step 2: Export a Volume → backup.tar.gz&#10;&lt;/h2&gt;&lt;p&gt;Run this &lt;strong&gt;one-line command&lt;/strong&gt; (replace nextcloud_data with your volume name):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;docker run --rm -v nextcloud_data:/data -v &amp;#34;$(pwd)&amp;#34;:/backup ubuntu \&#10; tar -czf /backup/nextcloud-data-backup.tar.gz -C /data ./&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What this does:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Mounts your volume to /data inside a temporary container&lt;/li&gt;&#10;&lt;li&gt;Mounts your current folder to /backup&lt;/li&gt;&#10;&lt;li&gt;Creates a compressed archive of the entire volume&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;After it finishes, you’ll have a file like:&#10;nextcloud-data-backup.tar.gz ← ready to download or move!&lt;/p&gt;&#10;&lt;p&gt;Pro tip: Add the date for clarity&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;docker run --rm -v nextcloud_data:/data -v &amp;#34;$(pwd)&amp;#34;:/backup ubuntu \&#10; tar -czf &amp;#34;/backup/nextcloud-data-$(date +%Y-%m-%d).tar.gz&amp;#34; -C /data ./&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="step-3-import-on-the-new-server"&gt;Step 3: Import on the New Server&#10;&lt;/h2&gt;&lt;ol&gt;&#10;&lt;li&gt;Copy your backup.tar.gz file to the new server (via scp, rsync, etc.)&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Then, &lt;strong&gt;create the empty volume&lt;/strong&gt; (important!):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;docker volume create nextcloud_data&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Run the import command (from the folder containing the backup file):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;docker run --rm -v nextcloud_data:/data -v &amp;#34;$(pwd)&amp;#34;:/backup ubuntu \&#10; tar -xzf /backup/nextcloud-data-2025-04-05.tar.gz -C /data&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Done! Your volume is now fully restored.&lt;/p&gt;&#10;&lt;p&gt;Never let Docker auto-create the volume during import — it can cause permission issues or merge problems.&lt;/p&gt;&#10;&lt;h2 id="bonus-using-external-volumes-with-docker-compose"&gt;Bonus: Using External Volumes with Docker Compose&#10;&lt;/h2&gt;&lt;p&gt;If you&amp;rsquo;re using docker-compose.yml, tell Docker that the volume is &lt;strong&gt;external&lt;/strong&gt; (already exists):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;services:&#10; nextcloud:&#10; image: nextcloud:latest&#10; volumes:&#10; - nextcloud_data:/var/www/html&#10;&#10;volumes:&#10; nextcloud_data:&#10; external: true&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Indentation matters — use &lt;strong&gt;exactly two spaces&lt;/strong&gt;.&lt;/p&gt;&#10;&lt;h2 id="real-world-use-cases"&gt;Real-World Use Cases&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;Migrating NextCloud to a new VPS&lt;/li&gt;&#10;&lt;li&gt;Backing up Vaultwarden before upgrading&lt;/li&gt;&#10;&lt;li&gt;Moving PhotoPrism library to a bigger server&lt;/li&gt;&#10;&lt;li&gt;Disaster recovery&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;This method is &lt;strong&gt;100% reliable&lt;/strong&gt;, works with any volume, and requires zero extra tools.&lt;/p&gt;&#10;&lt;p&gt;You now have a bulletproof Docker volume backup strategy.&lt;/p&gt;&#10;&lt;p&gt;Happy containerizing! 🐳&lt;/p&gt;&#10;&lt;p&gt;Thank you for reading!&lt;/p&gt;&#10;</description></item><item><title>How to easily self-host at home and put your projects online under CGNAT</title><link>https://aquasp.blog/how-to-easily-self-host-at-home-and-put-your-projects-online-under-cgnat/</link><pubDate>Tue, 09 Dec 2025 01:13:41 +0000</pubDate><guid>https://aquasp.blog/how-to-easily-self-host-at-home-and-put-your-projects-online-under-cgnat/</guid><description>&lt;h2 id="introduction"&gt;Introduction&#10;&lt;/h2&gt;&lt;p&gt;Want to run heavy services on a powerful server at home, even though your ISP puts you behind CGNAT? This guide shows you exactly how to put them online — the &lt;strong&gt;old-school, bulletproof way&lt;/strong&gt; using SSH reverse tunnels.&lt;/p&gt;&#10;&lt;p&gt;No Cloudflare Tunnel. No Ngrok. Just SSH + systemd.&lt;/p&gt;&#10;&lt;h2 id="the-downsides-and-why-theyre-manageable"&gt;The Downsides (and Why They’re Manageable)&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;Home internet isn’t datacenter-grade (outages happen)&lt;/li&gt;&#10;&lt;li&gt;Most ISPs use CGNAT → you can’t open ports normally&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Use a cheap VPS as a public “jump box”. Your heavy server stays home. The VPS only forwards ports.&lt;/p&gt;&#10;&lt;h2 id="how-it-works--the-magic-of-reverse-ssh-tunnels--r"&gt;How It Works – The Magic of Reverse SSH Tunnels (-R)&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;Internet → Cheap VPS (public IP) → SSH reverse tunnel → Your home server (behind CGNAT)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Your home server initiates an outbound SSH connection to the VPS and says:&#10;“Anything that hits port 8096 on you → send it to my local Jellyfin on 8096”&lt;/p&gt;&#10;&lt;p&gt;Zero ports opened on your home router. Zero exposure.&lt;/p&gt;&#10;&lt;h2 id="step-by-step-setup"&gt;Step-by-Step Setup&#10;&lt;/h2&gt;&lt;h3 id="1-on-your-home-server-the-powerful-one"&gt;1. On Your Home Server (the powerful one)&#10;&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Create folder for tunnel configs&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo mkdir -p /etc/sshtunnels&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Example: expose Jellyfin (port 8096)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo nano /etc/sshtunnels/jellyfin.conf&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Content of the file:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;8096:8096 # remote_port:local_port&#10;443:8443 # optional: HTTPS reverse proxy on VPS → your local 8443&#10;80:8080&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;One line per service. First number = port on the &lt;strong&gt;VPS&lt;/strong&gt;, second = port on &lt;strong&gt;your home server&lt;/strong&gt;.&lt;/p&gt;&#10;&lt;h3 id="2-generate-an-ssh-key-if-you-dont-have-one"&gt;2. Generate an SSH Key (if you don’t have one)&#10;&lt;/h3&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;ssh-keygen -t ed25519 -C &amp;#34;home-server-tunnel&amp;#34;&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Copy the public key to your VPS:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;ssh-copy-id user@your-vps-ip&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="3-create-the-tunnel-manager-script"&gt;3. Create the Tunnel Manager Script&#10;&lt;/h3&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;sudo nano /usr/local/bin/sshtunnel.sh&#10;&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;#!/bin/bash&#10;&#10;# === EDIT THESE ===&#10;REMOTE_USER=&amp;#34;root&amp;#34; # or your VPS user&#10;REMOTE_HOST=&amp;#34;123.45.67.89&amp;#34; # your VPS public IP&#10;SSH_KEY=&amp;#34;/home/youruser/.ssh/id_ed25519&amp;#34;&#10;SSH_PORT=&amp;#34;22&amp;#34; # change if you use a non-standard port&#10;# ==================&#10;&#10;INSTANCE=&amp;#34;$1&amp;#34;&#10;CONFIG_FILE=&amp;#34;/etc/sshtunnels/${INSTANCE}.conf&amp;#34;&#10;&#10;if [[ ! -f &amp;#34;$CONFIG_FILE&amp;#34; ]]; then&#10; echo &amp;#34;Error: Config file $CONFIG_FILE not found!&amp;#34;&#10; exit 1&#10;fi&#10;&#10;# Build -R arguments&#10;FORWARD_OPTS=&amp;#34;&amp;#34;&#10;while IFS=: read -r remote_port local_port; do&#10; [[ -z &amp;#34;$remote_port&amp;#34; || &amp;#34;$remote_port&amp;#34; =~ ^# ]] &amp;amp;&amp;amp; continue&#10; # Clean any old process using the remote port&#10; ssh -p &amp;#34;$SSH_PORT&amp;#34; &amp;#34;$REMOTE_USER@$REMOTE_HOST&amp;#34; \&#10; &amp;#34;lsof -i :$remote_port -t | xargs -r kill -9&amp;#34; 2&amp;gt;/dev/null&#10; FORWARD_OPTS=&amp;#34;$FORWARD_OPTS -R $remote_port:localhost:$local_port&amp;#34;&#10;done &amp;lt; &amp;#34;$CONFIG_FILE&amp;#34;&#10;&#10;echo &amp;#34;Starting tunnel $INSTANCE → $REMOTE_HOST ($FORWARD_OPTS)&amp;#34;&#10;&#10;exec ssh -o StrictHostKeyChecking=no \&#10; -o ServerAliveInterval=30 \&#10; -o ServerAliveCountThreshold=3 \&#10; -o ExitOnForwardFailure=yes \&#10; -o GatewayPorts=yes \&#10; -N -T \&#10; -i &amp;#34;$SSH_KEY&amp;#34; \&#10; -p &amp;#34;$SSH_PORT&amp;#34; \&#10; $FORWARD_OPTS \&#10; &amp;#34;$REMOTE_USER@$REMOTE_HOST&amp;#34;&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Make it executable:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;sudo chmod +x /usr/local/bin/sshtunnel.sh&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="4-create-a-systemd-service-auto-start--auto-reconnect"&gt;4. Create a Systemd Service (Auto-Start &amp;amp; Auto-Reconnect)&#10;&lt;/h3&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;sudo nano /etc/systemd/system/sshtunnel@.service&#10;&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;[Unit]&#10;Description=SSH Reverse Tunnel for %i&#10;After=network-online.target&#10;Wants=network-online.target&#10;&#10;[Service]&#10;User=youruser # ← change to your home user (not root!)&#10;Group=youruser&#10;ExecStart=/usr/local/bin/sshtunnel.sh %i&#10;Restart=always&#10;RestartSec=10&#10;&#10;[Install]&#10;WantedBy=multi-user.target&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Reload and enable:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;sudo systemctl daemon-reload&#10;&#10;# Start a tunnel (example: jellyfin)&#10;sudo systemctl enable --now sshtunnel@jellyfin.service&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Check status:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;sudo systemctl status sshtunnel@jellyfin.service&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="5-on-the-vps-side-optional-but-recommended"&gt;5. On the VPS Side (Optional but Recommended)&#10;&lt;/h3&gt;&lt;p&gt;Install a tiny web server or Caddy/nginx to terminate TLS and proxy to the forwarded ports.&lt;/p&gt;&#10;&lt;p&gt;Example with Caddy (automatic HTTPS):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;# On the VPS&#10;apt install caddy&#10;&#10;# /etc/caddy/Caddyfile&#10;jellyfin.yourdomain.com {&#10; reverse_proxy localhost:8096&#10;}&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now jellyfin.yourdomain.com → your home Jellyfin, fully encrypted.&lt;/p&gt;&#10;&lt;p&gt;All running on my beast home server behind CGNAT.&lt;/p&gt;&#10;&lt;h2 id="pros-of-this-setup"&gt;Pros of This Setup&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;Works behind any CGNAT / ISP block&lt;/li&gt;&#10;&lt;li&gt;No third-party dependency (no Cloudflare, no Ngrok)&lt;/li&gt;&#10;&lt;li&gt;Full encryption possible&lt;/li&gt;&#10;&lt;li&gt;Survives reboots (systemd + Restart=always)&lt;/li&gt;&#10;&lt;li&gt;Costs almost nothing&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="final-words"&gt;Final Words&#10;&lt;/h2&gt;&lt;p&gt;This is the one really cool way I’ve found to self-host heavy services at home in 2025.&lt;/p&gt;&#10;&lt;p&gt;Your powerful hardware stays home. Your $1/month VPS is just a traffic cop.&lt;/p&gt;&#10;&lt;p&gt;Thank you for reading — now go build your unstoppable home lab!&lt;/p&gt;&#10;</description></item><item><title>How to Automatically Backup Your Self-Hosted Ghost Blog</title><link>https://aquasp.blog/how-to-automatically-backup-your-self-hosted-ghost-blog/</link><pubDate>Tue, 09 Dec 2025 01:05:55 +0000</pubDate><guid>https://aquasp.blog/how-to-automatically-backup-your-self-hosted-ghost-blog/</guid><description>&lt;h2 id="introduction"&gt;Introduction&#10;&lt;/h2&gt;&lt;p&gt;Ghost is an &lt;strong&gt;incredibly fast and elegant&lt;/strong&gt; blogging platform. But unlike WordPress, it doesn’t have built-in one-click backup plugins.&lt;/p&gt;&#10;&lt;p&gt;That changes today.&lt;/p&gt;&#10;&lt;p&gt;In this guide, you’ll set up a &lt;strong&gt;fully automated daily backup system&lt;/strong&gt; that:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Dumps your MySQL database&lt;/li&gt;&#10;&lt;li&gt;Backs up all themes, images, and content&lt;/li&gt;&#10;&lt;li&gt;Compresses everything into a single &lt;code&gt;.zip&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Uploads it securely to your cloud storage (pCloud, NextCloud, Google Drive, Dropbox, etc.)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;All using &lt;strong&gt;free tools&lt;/strong&gt;: &lt;code&gt;rclone&lt;/code&gt; + a simple bash script + cron.&lt;/p&gt;&#10;&lt;p&gt;Let’s get started.&lt;/p&gt;&#10;&lt;h2 id="step-1-install-rclone"&gt;Step 1: Install Rclone&#10;&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo apt update &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo apt install -y rclone&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Rclone is the Swiss Army knife of cloud storage — it supports &lt;strong&gt;over 70 providers&lt;/strong&gt;.&lt;/p&gt;&#10;&lt;p&gt;Full list: &lt;a class="link" href="https://rclone.org/overview/?ref=aquasp.blog" target="_blank" rel="noopener"&#10; &gt;https://rclone.org/overview/&lt;/a&gt;&lt;/p&gt;&#10;&lt;h2 id="step-2-configure-rclone-connect-your-cloud-storage"&gt;Step 2: Configure Rclone (Connect Your Cloud Storage)&#10;&lt;/h2&gt;&lt;p&gt;Run:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;rclone config&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Follow the prompts:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;n → new remote&lt;/li&gt;&#10;&lt;li&gt;Name it something like ghost-backup or pcloud&lt;/li&gt;&#10;&lt;li&gt;Choose your provider (e.g., webdav for NextCloud, pcloud, google drive, etc.)&lt;/li&gt;&#10;&lt;li&gt;Enter your credentials/URL when asked&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Test it works:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;rclone ls ghost-backup:&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You should see your remote files (or an empty folder if new).&lt;/p&gt;&#10;&lt;p&gt;Type q to quit.&lt;/p&gt;&#10;&lt;h2 id="step-3-get-your-ghost-database-credentials"&gt;Step 3: Get Your Ghost Database Credentials&#10;&lt;/h2&gt;&lt;p&gt;Log in as your Ghost user (not root):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;su - yourghostuser&#10;cd /var/www/ghost # or wherever you installed Ghost&#10;cat config.production.json&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Look for the database section. You’ll see something like:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;#34;database&amp;#34;: {&#10; &amp;#34;client&amp;#34;: &amp;#34;mysql&amp;#34;,&#10; &amp;#34;connection&amp;#34;: {&#10; &amp;#34;host&amp;#34;: &amp;#34;localhost&amp;#34;,&#10; &amp;#34;user&amp;#34;: &amp;#34;ghost_db_user&amp;#34;,&#10; &amp;#34;password&amp;#34;: &amp;#34;yoursecretpassword&amp;#34;,&#10; &amp;#34;database&amp;#34;: &amp;#34;ghost_prod&amp;#34;&#10; }&#10;}&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Write down&lt;/strong&gt;:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Database name (ghost_prod)&lt;/li&gt;&#10;&lt;li&gt;Username (ghost_db_user)&lt;/li&gt;&#10;&lt;li&gt;Password&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="step-4-create-the-backup-script"&gt;Step 4: Create the Backup Script&#10;&lt;/h2&gt;&lt;p&gt;Create the script as root:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;nano /root/backup-ghost.sh&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Paste this (then edit the variables below):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;#!/bin/bash&#10;&#10;# === EDIT THESE VALUES ===&#10;GHOST_USER=&amp;#34;yourghostuser&amp;#34; # e.g. ghost&#10;GHOST_PATH=&amp;#34;/var/www/ghost&amp;#34; # path to your Ghost install&#10;DB_NAME=&amp;#34;ghost_prod&amp;#34; # from config.production.json&#10;DB_USER=&amp;#34;ghost_db_user&amp;#34; # from config.production.json&#10;DB_PASS=&amp;#34;yoursecretpassword&amp;#34; # from config.production.json&#10;BACKUP_NAME=&amp;#34;theselfhostingart-blog&amp;#34; # name for your backup zip&#10;RCLONE_REMOTE=&amp;#34;ghost-backup&amp;#34; # name you gave in rclone config&#10;RCLONE_PATH=&amp;#34;/&amp;#34; # folder in your cloud (use / for root)&#10;# =========================&#10;&#10;DATE=$(date +&amp;#39;%Y-%m-%d_%H-%M&amp;#39;)&#10;BACKUP_DIR=&amp;#34;/home/$GHOST_USER/backups/$DATE&amp;#34;&#10;ZIP_FILE=&amp;#34;$BACKUP_DIR/$BACKUP_NAME-$DATE.zip&amp;#34;&#10;&#10;echo &amp;#34;Starting Ghost backup: $DATE&amp;#34;&#10;&#10;# Create backup directory&#10;mkdir -p &amp;#34;$BACKUP_DIR&amp;#34;&#10;&#10;# Backup database&#10;echo &amp;#34;Backing up database...&amp;#34;&#10;mysqldump -u &amp;#34;$DB_USER&amp;#34; -p&amp;#34;$DB_PASS&amp;#34; --add-drop-table &amp;#34;$DB_NAME&amp;#34; | gzip &amp;gt; &amp;#34;$BACKUP_DIR/db.sql.gz&amp;#34;&#10;&#10;# Backup content folder (themes, images, etc.)&#10;echo &amp;#34;Backing up content folder...&amp;#34;&#10;rsync -av --exclude=&amp;#39;logs&amp;#39; --exclude=&amp;#39;cache&amp;#39; &amp;#34;$GHOST_PATH/content/&amp;#34; &amp;#34;$BACKUP_DIR/content/&amp;#34;&#10;&#10;# Compress everything&#10;echo &amp;#34;Compressing backup...&amp;#34;&#10;zip -r &amp;#34;$ZIP_FILE&amp;#34; &amp;#34;$BACKUP_DIR/content&amp;#34; &amp;#34;$BACKUP_DIR/db.sql.gz&amp;#34; &amp;gt; /dev/null&#10;&#10;# Upload to cloud&#10;echo &amp;#34;Uploading to cloud storage...&amp;#34;&#10;rclone copy &amp;#34;$ZIP_FILE&amp;#34; &amp;#34;$RCLONE_REMOTE:$RCLONE_PATH&amp;#34;&#10;&#10;# Cleanup: remove local backups older than 1 day (optional but recommended)&#10;echo &amp;#34;Cleaning up old local backups...&amp;#34;&#10;find /home/$GHOST_USER/backups -type d -mtime +1 -exec rm -rf {} +&#10;&#10;echo &amp;#34;Backup complete: $ZIP_FILE → $RCLONE_REMOTE:$RCLONE_PATH&amp;#34;&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Make it executable:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;chmod +x /root/backup-ghost.sh&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Test it manually first&lt;/strong&gt;:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;/root/backup-ghost.sh&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Check your cloud storage — you should see a file like:&#10;theselfhostingart-blog-2025-04-05_03-22.zip&lt;/p&gt;&#10;&lt;h2 id="step-5-automate-with-cron-daily-backups"&gt;Step 5: Automate with Cron (Daily Backups)&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;crontab -e&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Add this line for &lt;strong&gt;daily backup at 2:00 AM&lt;/strong&gt;:&lt;/p&gt;&#10;&lt;p&gt;cron&lt;code&gt;0 2 * * * /usr/bin/bash /root/backup-ghost.sh &amp;gt;&amp;gt; /var/log/ghost-backup.log 2&amp;gt;&amp;amp;1&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;Save and exit.&lt;/p&gt;&#10;&lt;p&gt;Your Ghost blog is now &lt;strong&gt;automatically backed up every day&lt;/strong&gt;.&lt;/p&gt;&#10;&lt;h2 id="whats-included-in-the-backup"&gt;What’s Included in the Backup?&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;Full database (posts, users, settings)&lt;/li&gt;&#10;&lt;li&gt;All uploaded images&lt;/li&gt;&#10;&lt;li&gt;Custom themes&lt;/li&gt;&#10;&lt;li&gt;Everything needed to restore or migrate&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;You can even send this .zip to Ghost(Pro) support — they can import it directly.&lt;/p&gt;&#10;&lt;h2 id="bonus-restore-in-case-of-disaster"&gt;Bonus: Restore in Case of Disaster&#10;&lt;/h2&gt;&lt;p&gt;To restore:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Install fresh Ghost&lt;/li&gt;&#10;&lt;li&gt;Unzip backup&lt;/li&gt;&#10;&lt;li&gt;Import DB: gunzip &amp;lt; db.sql.gz | mysql -u user -p dbname&lt;/li&gt;&#10;&lt;li&gt;Replace content/ folder&lt;/li&gt;&#10;&lt;li&gt;Run ghost restart&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="credits--thanks"&gt;Credits &amp;amp; Thanks&#10;&lt;/h2&gt;&lt;p&gt;This method is inspired and improved from this excellent post:&#10;&lt;a class="link" href="https://dev.to/kvizdos/how-to-automatically-backup-ghost-blogs-4he1?ref=aquasp.blog" target="_blank" rel="noopener"&#10; &gt;How to Automatically Backup Ghost Blogs – Kenton Vizdos&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;Thank you, Kenton!&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;p&gt;Your self-hosted blog now sleeps better at night. 😴💾&lt;/p&gt;&#10;&lt;p&gt;Thank you for reading!&lt;/p&gt;&#10;</description></item><item><title>How to install NextCloud with OpenLiteSpeed (LOMP stack)</title><link>https://aquasp.blog/how-to-install-nextcloud-with-openlitespeed-lomp-stack/</link><pubDate>Tue, 09 Dec 2025 00:59:45 +0000</pubDate><guid>https://aquasp.blog/how-to-install-nextcloud-with-openlitespeed-lomp-stack/</guid><description>&lt;h2 id="introduction"&gt;Introduction&#10;&lt;/h2&gt;&lt;p&gt;Today I’ll show you how to build what I genuinely believe is the &lt;strong&gt;fastest NextCloud stack&lt;/strong&gt; available in 2025:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;OpenLiteSpeed&lt;/strong&gt; – the fastest web server with built-in cache&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;LSPHP 8.1/8.2&lt;/strong&gt; – LiteSpeed’s ultra-fast PHP implementation&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Redis + APCu&lt;/strong&gt; – for blazing-fast caching and locking&lt;/li&gt;&#10;&lt;li&gt;Runs completely &lt;strong&gt;non-root&lt;/strong&gt;, under its own user&lt;/li&gt;&#10;&lt;li&gt;Hardened with proper security headers, HSTS, and isolated data folder&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Even if NextCloud gets compromised, the attacker still can’t touch the rest of your server.&lt;/p&gt;&#10;&lt;p&gt;Let’s go!&lt;/p&gt;&#10;&lt;h2 id="step-1-secure-your-vps-first"&gt;Step 1: Secure Your VPS First&#10;&lt;/h2&gt;&lt;p&gt;Before anything, harden your server. Follow my full guide here:&#10;&lt;a class="link" href="https://aquasp.blog/how-to-make-your-vps-secure/" &gt;How to Make Your VPS Secure&lt;/a&gt;&lt;/p&gt;&#10;&lt;h2 id="step-2-install-openlitespeed-lsphp-redis--tools"&gt;Step 2: Install OpenLiteSpeed, LSPHP, Redis &amp;amp; Tools&#10;&lt;/h2&gt;&lt;p&gt;Run as root:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Update system&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;apt update &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt upgrade -y&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Add OpenLiteSpeed repository&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;wget -O - https://repo.litespeed.sh | bash&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Install essentials&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;apt install -y curl gnupg2 imagemagick ffmpeg redis openlitespeed lsphp81* lsphp82* zip unzip mariadb-server mariadb-client&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;Note: On Ubuntu 22.04+, the ImageMagick package might be libmagickwand-dev + imagemagick. The above works on most recent Debian/Ubuntu.&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;Enable and restart Redis:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;systemctl enable --now redis-server&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="step-3-create-a-dedicated-system-user-for-nextcloud"&gt;Step 3: Create a Dedicated System User for NextCloud&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;adduser --shell /bin/bash files&#10;usermod -aG redis files # Allow access to Redis socket&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="step-4-download--extract-nextcloud-as-the-files-user"&gt;Step 4: Download &amp;amp; Extract NextCloud as the &amp;ldquo;files&amp;rdquo; User&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;su - files&#10;mkdir -p ~/public_html&#10;cd ~/public_html&#10;&#10;wget https://download.nextcloud.com/server/releases/latest.zip&#10;unzip latest.zip&#10;rsync -av nextcloud/ ./&#10;rm -rf nextcloud latest.zip .htaccess .user.ini&#10;exit&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="step-5-configure-openlitespeed-web-admin-port-7080"&gt;Step 5: Configure OpenLiteSpeed Web Admin (Port 7080)&#10;&lt;/h2&gt;&lt;p&gt;Set an admin password:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;/usr/local/lsws/admin/misc/admpass.sh&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now visit: https://your-vps-ip:7080 and log in.&lt;/p&gt;&#10;&lt;h3 id="virtual-host-setup"&gt;Virtual Host Setup&#10;&lt;/h3&gt;&lt;ol&gt;&#10;&lt;li&gt;Delete the default &amp;ldquo;Example&amp;rdquo; virtual host&lt;/li&gt;&#10;&lt;li&gt;Add new Virtual Host:&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Virtual Host Name: yourdomain.com&lt;/li&gt;&#10;&lt;li&gt;Virtual Host Root: /home/files/&lt;/li&gt;&#10;&lt;li&gt;Config File: $SERVER_ROOT/conf/vhosts/$VH_NAME/vhconf.conf&lt;/li&gt;&#10;&lt;li&gt;Document Root: $VH_ROOT/public_html&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;ol start="3"&gt;&#10;&lt;li&gt;Script Handler → Add:&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Suffix: php&lt;/li&gt;&#10;&lt;li&gt;Handler Type: LiteSpeed LVE&lt;/li&gt;&#10;&lt;li&gt;Handler: lsphp81 (or lsphp82 if you prefer PHP 8.2)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;ol start="4"&gt;&#10;&lt;li&gt;Rewrite Rules (force HTTPS):&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;RewriteEngine On&#10;RewriteCond %{HTTPS} !=on&#10;RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]&#10;&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;&#10;&lt;li&gt;Security Headers (Context → Static → Add new context /):&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;Strict-Transport-Security &amp;#34;max-age=63072000; includeSubDomains; preload&amp;#34;&#10;Content-Security-Policy &amp;#34;upgrade-insecure-requests&amp;#34;&#10;&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;&#10;&lt;li&gt;External App → LSPHP → Edit:&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Run as User/Group: files&lt;/li&gt;&#10;&lt;li&gt;PHP_LSAPI_CHILDREN = 100&lt;/li&gt;&#10;&lt;li&gt;LSAPI_AVOID_FORK = 0&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;ol start="2"&gt;&#10;&lt;li&gt;Listeners:&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Delete default listeners&lt;/li&gt;&#10;&lt;li&gt;Add HTTP → port 80&lt;/li&gt;&#10;&lt;li&gt;Add HTTPS → port 443 (Secure = Yes)&lt;/li&gt;&#10;&lt;li&gt;Map your domain to both listeners&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Graceful restart → OpenLiteSpeed → Graceful Restart&lt;/p&gt;&#10;&lt;h2 id="step-6-issue-lets-encrypt-ssl"&gt;Step 6: Issue Let’s Encrypt SSL&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;apt install -y certbot&#10;certbot certonly --webroot -w /home/files/public_html -d yourdomain.com&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note the paths (you’ll need them):&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Fullchain: /etc/letsencrypt/live/yourdomain.com/fullchain.pem&lt;/li&gt;&#10;&lt;li&gt;Privkey: /etc/letsencrypt/live/yourdomain.com/privkey.pem&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Add them in:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Virtual Host → SSL tab&lt;/li&gt;&#10;&lt;li&gt;Listener HTTPS → SSL tab&lt;/li&gt;&#10;&lt;li&gt;Chained Certificate = Yes&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Graceful restart again.&lt;/p&gt;&#10;&lt;h2 id="step-7-auto-renew-ssl"&gt;Step 7: Auto-Renew SSL&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;crontab -e&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Add:&lt;/p&gt;&#10;&lt;p&gt;cron&lt;code&gt;0 3 * * * /usr/bin/certbot renew --quiet&lt;/code&gt;&lt;/p&gt;&#10;&lt;h2 id="step-8-install--secure-mariadbmysql"&gt;Step 8: Install &amp;amp; Secure MariaDB/MySQL&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;mysql_secure_installation&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then create database &amp;amp; user:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;mysql -u root -p&#10;CREATE DATABASE nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;&#10;CREATE USER &amp;#39;ncuser&amp;#39;@&amp;#39;localhost&amp;#39; IDENTIFIED BY &amp;#39;strong-password-here&amp;#39;;&#10;GRANT ALL PRIVILEGES ON nextcloud.* TO &amp;#39;ncuser&amp;#39;@&amp;#39;localhost&amp;#39;;&#10;FLUSH PRIVILEGES;&#10;EXIT;&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="step-9-optimize-php--enable-opcache--apcu"&gt;Step 9: Optimize PHP &amp;amp; Enable OPCache + APCu&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;# Edit the correct php.ini (adjust path if using lsphp82)&#10;sed -i &amp;#39;/usr/local/lsws/lsphp81/etc/php/8.1/litespeed/php.ini&amp;#39; \&#10; -e &amp;#39;s/memory_limit = .*/memory_limit = 1024M/&amp;#39; \&#10; -e &amp;#39;s/upload_max_filesize = .*/upload_max_filesize = 10G/&amp;#39; \&#10; -e &amp;#39;s/post_max_size = .*/post_max_size = 10G/&amp;#39; \&#10; -e &amp;#39;s/max_execution_time = .*/max_execution_time = 3600/&amp;#39; \&#10; -e &amp;#39;s/opcache.enable=.*/opcache.enable=1/&amp;#39; \&#10; -e &amp;#39;s/;opcache.memory_consumption=.*/opcache.memory_consumption=512/&amp;#39; \&#10; -e &amp;#39;s/;opcache.interned_strings_buffer=.*/opcache.interned_strings_buffer=64/&amp;#39; \&#10; -e &amp;#39;s/;opcache.max_accelerated_files=.*/opcache.max_accelerated_files=20000/&amp;#39;&#10;&#10;# Enable APCu CLI&#10;echo &amp;#34;apc.enable_cli = 1&amp;#34; &amp;gt;&amp;gt; /usr/local/lsws/lsphp81/etc/php/8.1/litespeed/php.ini&#10;&#10;pkill -f lsphp&#10;systemctl restart lsws&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="step-10-configure-redis-as-unix-socket"&gt;Step 10: Configure Redis as Unix Socket&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;sed -i &amp;#39;s/port 6379/port 0/&amp;#39; /etc/redis/redis.conf&#10;sed -i &amp;#39;s|# unixsocket /var/run/redis/redis-server.sock|unixsocket /var/run/redis/redis-server.sock|&amp;#39; /etc/redis/redis.conf&#10;sed -i &amp;#39;s/# unixsocketperm 700/unixsocketperm 770/&amp;#39; /etc/redis/redis.conf&#10;sed -i &amp;#39;s/# maxmemory .*/maxmemory 1gb/&amp;#39; /etc/redis/redis.conf&#10;&#10;systemctl restart redis-server&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="step-11-final-nextcloud-configuration"&gt;Step 11: Final NextCloud Configuration&#10;&lt;/h2&gt;&lt;h3 id="move-data-folder-outside-web-root-critical"&gt;Move Data Folder Outside Web Root (Critical!)&#10;&lt;/h3&gt;&lt;p&gt;During setup, set data directory to: /home/files/data&lt;/p&gt;&#10;&lt;h3 id="edit-configphp-after-first-login"&gt;Edit config.php (after first login)&#10;&lt;/h3&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;su - files&#10;nano /home/files/public_html/config/config.php&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Add &lt;strong&gt;right after&lt;/strong&gt; &amp;lsquo;installed&amp;rsquo; =&amp;gt; true,:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;#39;memcache.local&amp;#39; =&amp;gt; &amp;#39;\\OC\\Memcache\\APCu&amp;#39;,&#10; &amp;#39;memcache.distributed&amp;#39; =&amp;gt; &amp;#39;\\OC\\Memcache\\Redis&amp;#39;,&#10; &amp;#39;memcache.locking&amp;#39; =&amp;gt; &amp;#39;\\OC\\Memcache\\Redis&amp;#39;,&#10; &amp;#39;redis&amp;#39; =&amp;gt; [&#10; &amp;#39;host&amp;#39; =&amp;gt; &amp;#39;/var/run/redis/redis-server.sock&amp;#39;,&#10; &amp;#39;port&amp;#39; =&amp;gt; 0,&#10; ],&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="set-up-background-jobs-cron"&gt;Set Up Background Jobs (Cron)&#10;&lt;/h3&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;su - files&#10;crontab -e&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Add:&lt;/p&gt;&#10;&lt;p&gt;cron&lt;code&gt;*/5 * * * * /usr/local/lsws/lsphp81/bin/php -f /home/files/public_html/cron.php&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;Then in NextCloud Admin → Basic Settings → Background jobs → Select &lt;strong&gt;Cron&lt;/strong&gt; (recommended).&lt;/p&gt;&#10;&lt;h2 id="bonus-make-occ-easy-to-use-forever"&gt;Bonus: Make occ Easy to Use Forever&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;su - files&#10;echo &amp;#34;alias occ=&amp;#39;/usr/local/lsws/lsphp81/bin/php /home/files/public_html/occ&amp;#39;&amp;#34; &amp;gt;&amp;gt; ~/.bashrc&#10;source ~/.bashrc&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now from anywhere in ~/public_html:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;cd ~/public_html&#10;occ status&#10;occ maintenance:repair&#10;occ db:add-missing-indices&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="youre-done"&gt;You&amp;rsquo;re Done!&#10;&lt;/h2&gt;&lt;p&gt;You now have:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;The &lt;strong&gt;fastest&lt;/strong&gt; NextCloud stack (OpenLiteSpeed + Redis + APCu)&lt;/li&gt;&#10;&lt;li&gt;Fully &lt;strong&gt;non-root&lt;/strong&gt; and isolated&lt;/li&gt;&#10;&lt;li&gt;Automatic SSL renewal&lt;/li&gt;&#10;&lt;li&gt;Hardened security headers&lt;/li&gt;&#10;&lt;li&gt;Proper data folder protection&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Enjoy your blazing-fast, private cloud!&lt;/p&gt;&#10;&lt;p&gt;Thank you for reading! 🚀&lt;/p&gt;&#10;</description></item><item><title>How to run your own monero node</title><link>https://aquasp.blog/how-to-run-your-own-monero-node/</link><pubDate>Tue, 09 Dec 2025 00:07:50 +0000</pubDate><guid>https://aquasp.blog/how-to-run-your-own-monero-node/</guid><description>&lt;h2 id="introduction"&gt;Introduction&#10;&lt;/h2&gt;&lt;p&gt;Monero is one of the most important cryptocurrencies in my opinion. It does not have the same market share as Bitcoin, but it is quite unique in one aspect: &lt;strong&gt;privacy&lt;/strong&gt;. Monero is just like cash — no one needs to know how much Monero was sent or who sent it. It&amp;rsquo;s the opposite of Bitcoin in this regard. In fact, Bitcoin is &lt;strong&gt;worse than fiat money&lt;/strong&gt; when it comes to privacy.&lt;/p&gt;&#10;&lt;p&gt;You can read more details here:&#10;&lt;a class="link" href="https://lukesmith.xyz/articles/monero-maximalism-or-how-bitcoin-is-a-coin/?ref=aquasp.blog" target="_blank" rel="noopener"&#10; &gt;https://lukesmith.xyz/articles/monero-maximalism-or-how-bitcoin-is-a-coin/&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Obs:&lt;/strong&gt; I&amp;rsquo;m &lt;strong&gt;NOT&lt;/strong&gt; recommending anyone invest in Monero. Monero is supposed to be a currency, but since crypto is still extremely volatile, many people treat it as an investment. Do your own research — I&amp;rsquo;m not responsible for any investments you make.&lt;/p&gt;&#10;&lt;h2 id="1-choose-a-vps-or-set-it-up-at-home"&gt;1. Choose a VPS or set it up at home&#10;&lt;/h2&gt;&lt;p&gt;First things first, you will need a server. You can use your own home PC if you prefer, or a VPS. A VPS is easier because it stays online 24/7 and you can always open the required ports.&lt;/p&gt;&#10;&lt;p&gt;At home, many ISPs block incoming ports, so your node wouldn’t be public (it would still help the network, but you couldn’t easily connect to it from outside without something like ngrok).&lt;/p&gt;&#10;&lt;p&gt;I personally recommend &lt;strong&gt;Contabo&lt;/strong&gt; for running a Monero node because they offer excellent prices on storage VPS plans.&#10;Check their pricing here: &lt;a class="link" href="https://contabo.com/en/storage-vps/?ref=aquasp.blog" target="_blank" rel="noopener"&#10; &gt;Contabo Storage VPS&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;The &lt;strong&gt;Storage VPS S&lt;/strong&gt; is more than enough. The Monero blockchain currently uses about 175 GB, less than 2 GB of RAM, and barely any CPU once fully synced (~3 % usage).&lt;/p&gt;&#10;&lt;h2 id="2-securing-the-vps"&gt;2. Securing the VPS&#10;&lt;/h2&gt;&lt;p&gt;First of all, &lt;strong&gt;disable password login&lt;/strong&gt;.&lt;/p&gt;&#10;&lt;p&gt;Then install and configure UFW (if it’s not already set up):&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo apt install ufw&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo ufw default deny incoming&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo ufw default allow outgoing&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo ufw allow ssh&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo ufw allow &lt;span style="color:#ae81ff"&gt;18080&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo ufw allow &lt;span style="color:#ae81ff"&gt;18089&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo ufw enable&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="3-creating-a-dedicated-user"&gt;3. Creating a dedicated user&#10;&lt;/h2&gt;&lt;p&gt;For security reasons, never run Monero as root. Create a normal user instead:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;adduser monerouser&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Set a password and press Enter through the rest of the prompts.&lt;/p&gt;&#10;&lt;h2 id="4-changing-settings-and-syncing-the-node"&gt;4. Changing settings and syncing the node&#10;&lt;/h2&gt;&lt;p&gt;Switch to the new user:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;su monerouser&#10;cd ~&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Download the official Monero CLI binaries (Linux 64-bit):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;wget -c https://downloads.getmonero.org/cli/monero-linux-x64-v0.18.3.4.tar.bz2&#10;mkdir monero&#10;tar -xjvf monero-linux-x64-v0.18.3.4.tar.bz2 -C monero --strip-components=1&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(Replace the version in the URL/filename with the latest one from &lt;a class="link" href="https://getmonero.org/?ref=aquasp.blog" target="_blank" rel="noopener"&#10; &gt;https://getmonero.org&lt;/a&gt; if needed.)&lt;/p&gt;&#10;&lt;p&gt;Enter the folder and start monerod once just to create the config files (stop it after a few seconds with Ctrl+C):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;cd monero&#10;./monerod&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now edit the configuration file:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;nano ~/.bitmonero/bitmonero.conf&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Paste the following recommended settings:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;# P2P full node&#10;public-node=true # Advertises the RPC-restricted port over p2p&#10;&#10;# RPC settings&#10;rpc-restricted-bind-ip=0.0.0.0&#10;rpc-restricted-bind-port=18089&#10;&#10;# Node settings&#10;enforce-dns-checkpointing=true&#10;db-sync-mode=safe # Slow but reliable db writes&#10;enable-dns-blocklist=true # Block known-malicious nodes&#10;no-igd=true # Disable UPnP&#10;no-zmq=true&#10;&#10;# Bandwidth settings (much faster sync + better contribution)&#10;out-peers=32&#10;in-peers=32&#10;limit-rate-up=1048576 # 1 GB/s upload&#10;limit-rate-down=1048576 # 1 GB/s download&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Save with &lt;strong&gt;Ctrl+O → Enter → Ctrl+X&lt;/strong&gt;.&lt;/p&gt;&#10;&lt;p&gt;Start the node in detached mode:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;./monerod --detach&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You&amp;rsquo;re done! Now just wait for it to fully sync.&lt;/p&gt;&#10;&lt;p&gt;Check sync status anytime with:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;./monerod status&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The initial sync usually takes a few hours depending on your connection and VPS speed.&lt;/p&gt;&#10;&lt;p&gt;Once it&amp;rsquo;s fully synced, you can connect any Monero wallet (Cake Wallet, Monero GUI, Feather, etc.) to your own node using your VPS IP and port &lt;strong&gt;18089&lt;/strong&gt;.&lt;/p&gt;&#10;&lt;h2 id="conclusion--credits"&gt;Conclusion &amp;amp; Credits&#10;&lt;/h2&gt;&lt;p&gt;That&amp;rsquo;s it! Running your own full node is strongly encouraged by the Monero community. It makes the network more decentralized and gives you maximum privacy.&lt;/p&gt;&#10;&lt;p&gt;This guide wouldn’t have been possible without these excellent resources:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://www.getmonero.org/resources/user-guides/vps_run_node.html?ref=aquasp.blog" target="_blank" rel="noopener"&#10; &gt;https://www.getmonero.org/resources/user-guides/vps_run_node.html&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://www.coincashew.com/coins/overview-xmr/guide-or-how-to-run-a-full-node?ref=aquasp.blog" target="_blank" rel="noopener"&#10; &gt;https://www.coincashew.com/coins/overview-xmr/guide-or-how-to-run-a-full-node&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Thank you for reading! 🙂&lt;/p&gt;&#10;</description></item><item><title>How to Self Host your own Piped Instance</title><link>https://aquasp.blog/how-to-self-host-your-own-piped-instance/</link><pubDate>Mon, 08 Dec 2025 23:58:10 +0000</pubDate><guid>https://aquasp.blog/how-to-self-host-your-own-piped-instance/</guid><description>&lt;h2 id="introduction"&gt;Introduction&#10;&lt;/h2&gt;&lt;p&gt;Piped is a privacy-first, open-source alternative YouTube front-end. No Google tracking, no ads (even on videos that normally have unskippable ones), and it works perfectly with SponsorBlock and dearrow.&lt;/p&gt;&#10;&lt;p&gt;Self-hosting your own instance is incredibly easy with Docker and takes less than 20 minutes.&lt;/p&gt;&#10;&lt;h2 id="requirements"&gt;Requirements&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;A domain (or subdomain)&lt;/li&gt;&#10;&lt;li&gt;A cheap KVM VPS with Docker support (avoid OpenVZ — old kernel)&lt;/li&gt;&#10;&lt;li&gt;~$3–6/month is more than enough (1 CPU, 1–2 GB RAM)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Great cheap providers in 2025:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;RackNerd&lt;/li&gt;&#10;&lt;li&gt;Hostinger (my affiliate if you want to support &lt;a class="link" href="https://hostinger.com.br/?REFERRALCODE=waterdownfall&amp;amp;ref=aquasp.blog" target="_blank" rel="noopener"&#10; &gt;https://hostinger.com.br?REFERRALCODE=waterdownfall&lt;/a&gt;)&lt;/li&gt;&#10;&lt;li&gt;Cloudcone, Hetzner Cloud, BuyVM, etc.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="step-1-secure--prepare-your-vps"&gt;Step 1: Secure &amp;amp; Prepare Your VPS&#10;&lt;/h2&gt;&lt;p&gt;(SSH keys only, firewall, etc. — do this first!)&lt;/p&gt;&#10;&lt;p&gt;Then install Docker (Ubuntu/Debian example):&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;apt update &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt upgrade -y&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;apt install -y ca-certificates curl gnupg lsb-release&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Add Docker repo&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo mkdir -p /etc/apt/keyrings&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;deb [arch=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;dpkg --print-architecture&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt; signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu &lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;lsb_release -cs&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt; stable&amp;#34;&lt;/span&gt; &amp;gt; /etc/apt/sources.list.d/docker.list&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;apt update&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="step-2-deploy-piped-with-the-official-docker-setup"&gt;Step 2: Deploy Piped with the Official Docker Setup&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;cd /opt&#10;git clone https://github.com/TeamPiped/Piped-Docker&#10;cd Piped-Docker&#10;./configure-instance.sh&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;During the script:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;Choose &lt;strong&gt;Caddy&lt;/strong&gt; as reverse proxy (easiest + automatic SSL)&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;Enter your domain and subdomains:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Frontend → piped.yourdomain.com&lt;/li&gt;&#10;&lt;li&gt;Backend API → pipedapi.yourdomain.com&lt;/li&gt;&#10;&lt;li&gt;Proxy → pipedproxy.yourdomain.com&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="step-3-point-dns-to-your-vps"&gt;Step 3: Point DNS to Your VPS&#10;&lt;/h2&gt;&lt;p&gt;Create three A records at your DNS provider:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;piped.yourdomain.com → VPS_IP&#10;pipedapi.yourdomain.com → VPS_IP&#10;pipedproxy.yourdomain.com → VPS_IP&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="step-4-launch-everything"&gt;Step 4: Launch Everything&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;docker compose up -d&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That’s it!&#10;After DNS propagates (usually &amp;lt; 10 minutes), your private YouTube will be live at:&lt;/p&gt;&#10;&lt;p&gt;&lt;a class="link" href="https://piped.yourdomain.com/?ref=aquasp.blog" target="_blank" rel="noopener"&#10; &gt;https://piped.yourdomain.com&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;Caddy automatically handles free Let’s Encrypt SSL — no manual certbot needed.&lt;/p&gt;&#10;&lt;h2 id="step-5-optional-check-logs"&gt;Step 5: (Optional) Check Logs&#10;&lt;/h2&gt;&lt;p&gt;Bash&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;# See what Caddy is doing&#10;docker logs -f caddy&#10;&#10;# Or any other container&#10;docker logs -f piped-frontend&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="bonus-tips"&gt;Bonus Tips&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;Want to make it public? Just share the URL — anyone can use your instance.&lt;/li&gt;&#10;&lt;li&gt;Want it private? Block it with Cloudflare firewall rules or basic auth in Caddy.&lt;/li&gt;&#10;&lt;li&gt;Pair it with the &lt;strong&gt;LibreTube&lt;/strong&gt; (Android) app for the full de-Googled experience.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;You now have your own ad-free, tracking-free, SponsorBlock-enabled YouTube — fully under your control.&lt;/p&gt;&#10;&lt;p&gt;Thanks for reading!&lt;/p&gt;&#10;</description></item><item><title>How to setup WordPress on LEMP with Redis and WP CLI on Debian 11</title><link>https://aquasp.blog/how-to-setup-wordpress-on-lemp-with-redis-and-wp-cli-on-debian-11/</link><pubDate>Mon, 08 Dec 2025 23:47:36 +0000</pubDate><guid>https://aquasp.blog/how-to-setup-wordpress-on-lemp-with-redis-and-wp-cli-on-debian-11/</guid><description>&lt;h2 id="introduction"&gt;Introduction&#10;&lt;/h2&gt;&lt;p&gt;This is the fastest, most secure, and most resource-efficient way to self-host WordPress in 2025.&#10;We’ll use a proper LEMP stack (Linux + Nginx + MySQL/MariaDB + PHP-FPM) with per-site PHP isolation, Redis object caching, automatic SSL, and WP-CLI — everything tuned for speed and security.&lt;/p&gt;&#10;&lt;p&gt;Let’s go.&lt;/p&gt;&#10;&lt;h2 id="step-0-secure--update-your-vps"&gt;Step 0: Secure &amp;amp; Update Your VPS&#10;&lt;/h2&gt;&lt;p&gt;(If you haven’t already, follow a VPS hardening guide first — SSH keys only, firewall, fail2ban, etc.)&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;apt update &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt upgrade -y&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;apt autoremove --purge&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;reboot&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="step-1-install-the-core-stack"&gt;Step 1: Install the Core Stack&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;# Nginx&#10;apt install nginx -y&#10;systemctl enable nginx&#10;&#10;# MariaDB (better than MySQL on Debian)&#10;apt install mariadb-server -y&#10;systemctl enable mariadb&#10;&#10;# PHP 8.3 + all needed extensions (using ondrej/sury repo)&#10;apt install ca-certificates apt-transport-https lsb-release -y&#10;wget -qO- https://packages.sury.org/php/apt.gpg | gpg --dearmor &amp;gt; /usr/share/keyrings/sury-php.gpg&#10;echo &amp;#34;deb [signed-by=/usr/share/keyrings/sury-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main&amp;#34; &amp;gt; /etc/apt/sources.list.d/sury-php.list&#10;apt update&#10;apt install php8.3-fpm php8.3-mysql php8.3-curl php8.3-gd php8.3-mbstring php8.3-xml php8.3-zip php8.3-intl php8.3-imagick php8.3-redis -y&#10;systemctl enable php8.3-fpm&#10;&#10;# Redis&#10;curl -fsSL https://packages.redis.io/gpg | gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg&#10;echo &amp;#34;deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main&amp;#34; &amp;gt; /etc/apt/sources.list.d/redis.list&#10;apt update &amp;amp;&amp;amp; apt install redis-server -y&#10;systemctl enable redis-server&#10;&#10;# Certbot + WP-CLI&#10;apt install python3-certbot-nginx -y&#10;curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar&#10;chmod +x wp-cli.phar &amp;amp;&amp;amp; mv wp-cli.phar /usr/local/bin/wp&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="step-2-secure-mariadb--create-database"&gt;Step 2: Secure MariaDB &amp;amp; Create Database&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;mysql_secure_installation&#10;&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;mysql -u root -p&#10;CREATE DATABASE wp_yoursite;&#10;CREATE USER &amp;#39;wp_yoursite&amp;#39;@&amp;#39;localhost&amp;#39; IDENTIFIED BY &amp;#39;strongpassword&amp;#39;;&#10;GRANT ALL ON wp_yoursite.* TO &amp;#39;wp_yoursite&amp;#39;@&amp;#39;localhost&amp;#39;;&#10;FLUSH PRIVILEGES;&#10;EXIT;&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="step-3-isolate-php-fpm-per-site-security--stability"&gt;Step 3: Isolate PHP-FPM Per Site (Security + Stability)&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;cd /etc/php/8.3/fpm/pool.d/&#10;cp www.conf yoursite.conf&#10;nano yoursite.conf&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Replace:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;[www] → [yoursite]&lt;/li&gt;&#10;&lt;li&gt;user = www-data → user = yoursiteuser (we’ll create this user soon)&lt;/li&gt;&#10;&lt;li&gt;group = www-data → group = yoursiteuser&lt;/li&gt;&#10;&lt;li&gt;listen = /run/php/php8.3-fpm.sock → listen = /run/php/php8.3-fpm-yoursite.sock&lt;/li&gt;&#10;&lt;li&gt;Change process manager from dynamic → ondemand (saves RAM)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;systemctl restart php8.3-fpm&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(It will fail until the user exists — that’s fine.)&lt;/p&gt;&#10;&lt;h2 id="step-4-optimize-php--enable-opcache"&gt;Step 4: Optimize PHP &amp;amp; Enable OPcache&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;sed -i &amp;#34;s/memory_limit = .*/memory_limit = 1024M/&amp;#34; /etc/php/8.3/fpm/php.ini&#10;sed -i &amp;#34;s/upload_max_filesize = .*/upload_max_filesize = 10240M/&amp;#34; /etc/php/8.3/fpm/php.ini&#10;sed -i &amp;#34;s/post_max_size = .*/post_max_size = 10240M/&amp;#34; /etc/php/8.3/fpm/php.ini&#10;sed -i &amp;#34;s/max_execution_time = .*/max_execution_time = 600/&amp;#34; /etc/php/8.3/fpm/php.ini&#10;sed -i &amp;#34;s/;opcache.enable=1/opcache.enable=1/&amp;#34; /etc/php/8.3/fpm/php.ini&#10;sed -i &amp;#34;s/;opcache.memory_consumption=.*/opcache.memory_consumption=512/&amp;#34; /etc/php/8.3/fpm/php.ini&#10;sed -i &amp;#34;s/;opcache.max_accelerated_files=.*/opcache.max_accelerated_files=20000/&amp;#34; /etc/php/8.3/fpm/php.ini&#10;sed -i &amp;#34;s/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/&amp;#34; /etc/php/8.3/fpm/php.ini&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="step-5-create-system-user--site-directory"&gt;Step 5: Create System User &amp;amp; Site Directory&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;adduser yoursiteuser --shell /bin/bash&#10;su yoursiteuser&#10;mkdir ~/public_html &amp;amp;&amp;amp; cd ~/public_html&#10;echo &amp;#34;cd ~/public_html&amp;#34; &amp;gt;&amp;gt; ~/.bashrc&#10;exit&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="step-6-nginx-config-fast--secure"&gt;Step 6: Nginx Config (Fast &amp;amp; Secure)&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;nano /etc/nginx/sites-available/yoursite.conf&#10;&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;upstream php-yoursite {&#10; server unix:/run/php/php8.3-fpm-yoursite.sock;&#10;}&#10;&#10;server {&#10; listen 80;&#10; listen [::]:80;&#10; server_name yourdomain.com www.yourdomain.com;&#10; root /home/yoursiteuser/public_html;&#10; index index.php index.html;&#10;&#10; client_max_body_size 10G;&#10;&#10; location / {&#10; try_files $uri $uri/ /index.php?$args;&#10; }&#10;&#10; location ~ \.php$ {&#10; include fastcgi_params;&#10; fastcgi_pass php-yoursite;&#10; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;&#10; }&#10;&#10; location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?|ttf|eot)$ {&#10; expires max;&#10; log_not_found off;&#10; }&#10;}&#10;&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;ln -s /etc/nginx/sites-available/yoursite.conf /etc/nginx/sites-enabled/&#10;nginx -t &amp;amp;&amp;amp; systemctl reload nginx&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="step-7-install-wordpress-via-wp-cli-as-the-site-user"&gt;Step 7: Install WordPress via WP-CLI (as the site user)&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;su yoursiteuser&#10;cd ~/public_html&#10;&#10;wp core download&#10;wp config create --dbname=wp_yoursite --dbuser=wp_yoursite --dbpass=&amp;#39;strongpassword&amp;#39; --locale=en_US&#10;wp core install --url=https://yourdomain.com --title=&amp;#34;Your Site&amp;#34; --admin_user=admin --admin_password=&amp;#39;strongpass&amp;#39; --admin_email=you@domain.com&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="step-8-ssl-with-lets-encrypt-auto-renew"&gt;Step 8: SSL with Let’s Encrypt (Auto-renew)&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;certbot --nginx -d yourdomain.com -d www.yourdomain.com&#10;# Choose redirect to HTTPS when asked&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Add auto-renew cron:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;crontab -e&#10;# Add:&#10;0 0 * * 0 certbot renew --quiet&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="step-9-enable-redis-object-cache"&gt;Step 9: Enable Redis Object Cache&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;# As root&#10;usermod -aG redis yoursiteuser&#10;chmod 770 /var/run/redis/redis-server.sock&#10;&#10;# Optimize Redis config&#10;sed -i &amp;#39;s/port 6379/port 0/&amp;#39; /etc/redis/redis.conf&#10;sed -i &amp;#39;s|# unixsocket /run/redis/redis-server.sock|unixsocket /var/run/redis/redis-server.sock|&amp;#39; /etc/redis/redis.conf&#10;sed -i &amp;#39;s/# unixsocketperm 700/unixsocketperm 770/&amp;#39; /etc/redis/redis.conf&#10;sed -i &amp;#39;s/# maxmemory .*/maxmemory 1024mb/&amp;#39; /etc/redis/redis.conf&#10;systemctl restart redis-server&#10;&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;# As yoursiteuser&#10;cd ~/public_html&#10;wp plugin install redis-cache --activate&#10;wp config set WP_REDIS_SCHEME unix&#10;wp config set WP_REDIS_PATH &amp;#39;/var/run/redis/redis-server.sock&amp;#39;&#10;wp redis enable&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="done"&gt;Done!&#10;&lt;/h2&gt;&lt;p&gt;You now have:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Fully isolated PHP-FPM pool (1 user = 1 site = no cross-site damage)&lt;/li&gt;&#10;&lt;li&gt;Redis object caching over Unix socket&lt;/li&gt;&#10;&lt;li&gt;OPcache + huge upload limits&lt;/li&gt;&#10;&lt;li&gt;Automatic SSL renewal&lt;/li&gt;&#10;&lt;li&gt;Fastest possible Nginx routing&lt;/li&gt;&#10;&lt;li&gt;WP-CLI ready&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Your wp-admin will feel instant, and the site will handle traffic like a champ — even on a $5/month VPS.&lt;/p&gt;&#10;&lt;p&gt;Next steps:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Install a page cache plugin (any free one is ok)&lt;/li&gt;&#10;&lt;li&gt;Set up Cloudflare (optional but recommended)&lt;/li&gt;&#10;&lt;li&gt;Regular backups&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Enjoy your blazing-fast, private WordPress setup!&lt;/p&gt;&#10;&lt;p&gt;Thanks for reading! 😊&lt;/p&gt;&#10;</description></item><item><title>How to setup your own email server</title><link>https://aquasp.blog/how-to-setup-your-own-email-server/</link><pubDate>Mon, 08 Dec 2025 23:35:02 +0000</pubDate><guid>https://aquasp.blog/how-to-setup-your-own-email-server/</guid><description>&lt;h2 id="introduction"&gt;Introduction&#10;&lt;/h2&gt;&lt;p&gt;Want your own ultra-private email like &lt;a class="link" href="" &gt;name@yourdomain.com&lt;/a&gt; with a beautiful webmail interface?&#10;This guide walks you through setting up a full mail server in under an hour using Luke Smith’s legendary &lt;strong&gt;EmailWiz&lt;/strong&gt; script + &lt;strong&gt;Roundcube&lt;/strong&gt; webmail — all on a $2–3/month VPS.&lt;/p&gt;&#10;&lt;p&gt;Everything is free, open-source, and 100% under your control.&lt;/p&gt;&#10;&lt;h2 id="step-0-grab-a-cheap-vps"&gt;Step 0: Grab a Cheap VPS&#10;&lt;/h2&gt;&lt;p&gt;Good providers with frequent sales:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Contabo&lt;/li&gt;&#10;&lt;li&gt;LowEndTalk “Offers” section&lt;/li&gt;&#10;&lt;li&gt;Hostinger (my affiliate if you want to support me -&amp;gt; &lt;a class="link" href="https://hostinger.com.br/?REFERRALCODE=waterdownfall&amp;amp;ref=aquasp.blog" target="_blank" rel="noopener"&#10; &gt;https://hostinger.com.br?REFERRALCODE=waterdownfall&lt;/a&gt;)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Requirements:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Any location (USA works fine)&lt;/li&gt;&#10;&lt;li&gt;Debian 10 or 11 (we’ll use Debian 10 in this guide)&lt;/li&gt;&#10;&lt;li&gt;At least 1 GB RAM (2 GB+ recommended)&lt;/li&gt;&#10;&lt;li&gt;Set hostname during signup to your domain (e.g., sobremail.com)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Wait for deployment → grab root password from email → SSH in.&lt;/p&gt;&#10;&lt;h2 id="step-1-basic-vps-hardening"&gt;Step 1: Basic VPS Hardening&#10;&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh root@your-vps-ip&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;apt update &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt upgrade -y&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Change root password:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;passwd&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Create and upload an SSH key (do this from your local machine):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;ssh-copy-id root@your-vps-ip&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now disable password login:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;nano /etc/ssh/sshd_config&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Change:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;PasswordAuthentication no&#10;UsePAM no&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;systemctl restart sshd&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Only your SSH key works now — much safer.&lt;/p&gt;&#10;&lt;h2 id="step-2-install-emailwiz-the-magic-script"&gt;Step 2: Install EmailWiz (the magic script)&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;apt install curl nginx python3-certbot-nginx -y&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Point these DNS records to your VPS IP:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;yourdomain.com → VPS IP (A record)&lt;/li&gt;&#10;&lt;li&gt;mail.yourdomain.com → VPS IP (A record)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Run Luke’s script:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;curl -LO lukesmith.xyz/emailwiz.sh&#10;sh emailwiz.sh&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Follow the prompts:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Say Yes/Y to everything&lt;/li&gt;&#10;&lt;li&gt;When asked for “System mail name” → enter ONLY yourdomain.com (NOT mail.yourdomain.com!)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Wait ~5–10 minutes. When it finishes, it gives you three DNS records to add:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;DKIM TXT record (mail._domainkey.yourdomain.com)&lt;/li&gt;&#10;&lt;li&gt;DMARC TXT record (_dmarc.yourdomain.com)&lt;/li&gt;&#10;&lt;li&gt;SPF TXT record (root domain)&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Add them at your DNS provider (Cloudflare, Namecheap, etc.).&lt;/p&gt;&#10;&lt;h2 id="step-3-set-up-reverse-dns-critical-for-deliverability"&gt;Step 3: Set Up Reverse DNS (Critical for Deliverability!)&#10;&lt;/h2&gt;&lt;p&gt;In Cloudcone panel → Networking → rDNS → set to yourdomain.com&#10;&lt;strong&gt;Do NOT enable IPv6&lt;/strong&gt; (Cloudcone doesn’t support IPv6 rDNS yet — it will hurt deliverability).&lt;/p&gt;&#10;&lt;h2 id="step-4-create-your-first-mailbox"&gt;Step 4: Create Your First Mailbox&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;useradd -G mail -m yourusername&#10;passwd yourusername&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Your email is now: &lt;a class="link" href="mailto:yourusername@yourdomain.com" &gt;yourusername@yourdomain.com&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;Test in Thunderbird/IMAP client:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;IMAP: mail.yourdomain.com (port 993, SSL/TLS)&lt;/li&gt;&#10;&lt;li&gt;SMTP: mail.yourdomain.com (port 465, SSL/TLS)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="step-5-install-roundcube-webmail"&gt;Step 5: Install Roundcube Webmail&#10;&lt;/h2&gt;&lt;p&gt;Add backports + PHP 8.x repo:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;apt install -y lsb-release ca-certificates apt-transport-https software-properties-common gnupg2&#10;echo &amp;#34;deb https://packages.sury.org/php/ $(lsb_release -sc) main&amp;#34; | tee /etc/apt/sources.list.d/sury-php.list&#10;wget -qO - https://packages.sury.org/php/apt.gpg | apt-key add -&#10;apt update&#10;apt install -y php8.0-fpm php8.0-common php8.0-gd php8.0-imap php8.0-mysql php8.0-curl php8.0-zip php8.0-xml php8.0-mbstring php8.0-intl mariadb-server&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Secure MySQL:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;mysql_secure_installation&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Create Roundcube database:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;mysql -u root -p&#10;CREATE DATABASE roundcube;&#10;CREATE USER &amp;#39;roundcubeuser&amp;#39;@&amp;#39;localhost&amp;#39; IDENTIFIED BY &amp;#39;strongpassword&amp;#39;;&#10;GRANT ALL ON roundcube.* TO &amp;#39;roundcubeuser&amp;#39;@&amp;#39;localhost&amp;#39;;&#10;FLUSH PRIVILEGES;&#10;EXIT;&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Download &amp;amp; extract Roundcube (latest complete version):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;cd /var/www&#10;wget https://github.com/roundcube/roundcubemail/releases/download/1.6.9/roundcubemail-1.6.9-complete.tar.gz&#10;tar xvf roundcubemail-1.6.9-complete.tar.gz&#10;mv roundcubemail-1.6.9 roundcube&#10;rm roundcubemail-1.6.9-complete.tar.gz&#10;chown -R www-data:www-data /var/www/roundcube/temp /var/www/roundcube/logs&#10;mysql roundcube &amp;lt; /var/www/roundcube/SQL/mysql.initial.sql&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Nginx config for Roundcube (/etc/nginx/sites-enabled/roundcube):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;server {&#10; listen 80;&#10; listen [::]:80;&#10; server_name yourdomain.com;&#10; root /var/www/roundcube;&#10; index index.php;&#10;&#10; location / {&#10; try_files $uri $uri/ /index.php;&#10; }&#10;&#10; location ~ \.php$ {&#10; include fastcgi_params;&#10; fastcgi_pass unix:/run/php/php8.0-fpm.sock;&#10; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;&#10; }&#10;&#10; location ~* \.(jpg|jpeg|gif|png|webp|svg|woff|woff2|ttf|css|js|ico|xml)$ {&#10; expires 360d;&#10; access_log off;&#10; }&#10;}&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Test &amp;amp; reload Nginx, then get SSL:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;nginx -t &amp;amp;&amp;amp; systemctl reload nginx&#10;certbot --nginx -d yourdomain.com&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Visit &lt;a class="link" href="https://yourdomain.com/installer" target="_blank" rel="noopener"&#10; &gt;https://yourdomain.com/installer&lt;/a&gt; → follow the wizard:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Database: roundcube, user roundcubeuser, password you set&lt;/li&gt;&#10;&lt;li&gt;IMAP host: localhost&lt;/li&gt;&#10;&lt;li&gt;SMTP host: localhost&lt;/li&gt;&#10;&lt;li&gt;Default host: mail.yourdomain.com&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Enable all plugins &lt;strong&gt;except Enigma&lt;/strong&gt; (it breaks identities in older versions).&lt;/p&gt;&#10;&lt;p&gt;After finishing, delete the installer:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;rm -rf /var/www/roundcube/installer&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="step-6-quality-of-life-tweaks"&gt;Step 6: Quality-of-Life Tweaks&#10;&lt;/h2&gt;&lt;p&gt;Edit /var/www/roundcube/config/config.inc.php:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;// Login with just username (no need to type @domain.com)&#10;$config[&amp;#39;username_domain&amp;#39;] = &amp;#39;yourdomain.com&amp;#39;;&#10;&#10;// Stay logged in for 6 months&#10;$config[&amp;#39;session_lifetime&amp;#39;] = 259200;&#10;&#10;// Disable Enigma if you enabled it&#10;// Remove &amp;#39;enigma&amp;#39; from $config[&amp;#39;plugins&amp;#39;] array&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Increase attachment size:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;nano /etc/php/8.0/fpm/php.ini&#10;&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;upload_max_filesize = 50M&#10;post_max_size = 50M&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;systemctl restart php8.0-fpm&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="step-7-brute-force-protection-with-fail2ban"&gt;Step 7: Brute-Force Protection with Fail2Ban&#10;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;apt install fail2ban -y&#10;cd /var/www/roundcube/plugins&#10;wget https://github.com/texxasrulez/roundcube_fail2ban/archive/refs/tags/1.4.zip&#10;unzip 1.4.zip&#10;mv roundcube_fail2ban-1.4 fail2ban&#10;rm 1.4.zip&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Enable in Roundcube config (config.inc.php):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;$config[&amp;#39;plugins&amp;#39;][] = &amp;#39;fail2ban&amp;#39;;&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Add jail (/etc/fail2ban/jail.local – create if missing):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;[roundcube]&#10;enabled = true&#10;port = http,https&#10;filter = roundcube&#10;action = iptables-multiport[name=roundcube, port=&amp;#34;http,https&amp;#34;]&#10;logpath = /var/www/roundcube/logs/errors.log&#10;maxretry = 5&#10;bantime = 3600&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Create filter (/etc/fail2ban/filter.d/roundcube.conf):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;[Definition]&#10;failregex = IMAP Error: Login failed for .* from &amp;lt;HOST&amp;gt;&#10;ignoreregex =&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Restart:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;systemctl restart fail2ban php8.0-fpm&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Done! Your webmail is now protected.&lt;/p&gt;&#10;&lt;h2 id="final-result"&gt;Final Result&#10;&lt;/h2&gt;&lt;p&gt;You now have:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Full email server with DKIM, SPF, DMARC&lt;/li&gt;&#10;&lt;li&gt;Beautiful, fast Roundcube webmail&lt;/li&gt;&#10;&lt;li&gt;Zero Google/Microsoft involvement&lt;/li&gt;&#10;&lt;li&gt;Login once every 6 months&lt;/li&gt;&#10;&lt;li&gt;Brute-force protection&lt;/li&gt;&#10;&lt;li&gt;All for ~$50/year&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Welcome to real email freedom.&lt;/p&gt;&#10;&lt;p&gt;Thanks for reading! 😊&lt;/p&gt;&#10;</description></item></channel></rss>