
There comes a point in every homelab journey where you look at your pile of
bash scripts, hand-crafted config files, and sticky notes that say things like
“remember to chown /data before starting” — and you decide:
enough. 😅
This is that post. Today I’m publishing the Ansible playbooks I’ve been using to deploy
and operate Nextcloud ☁️ and WordPress 📝 on a
single-node K3s cluster running on AlmaLinux 9.
Everything is infrastructure-as-code, fully idempotent,
and comes with a complete operations guide.
But first, a brief word from our sponsors — by which I mean:
a moment of involuntary self-reflection. 🙂
🎉 Sidebar: Wait, this blog has a birthday?
Yesterday marked exactly one year since the very first post went live on this blog.
It contained exactly two words: Hallo Welt.
Which is German for Hello World,
which is programmer for:“I have absolutely no idea what I’m doing yet, but I have a domain, a VPS,
and bygit pushI will use both.” 🚀Frankly, I did not expect this blog to still exist a year later.
Most of my side projects follow a very predictable lifecycle:
git init→ burst of enthusiasm → three commits →
git stash→ forgotten forever 💀Yet here we are at one year uptime, which in personal-project terms
is roughly equivalent to a Fortune 500 company celebrating its centennial.The metrics are accordingly impressive:
- 📚 several posts
- 👀 at least two readers (one of whom is me checking mobile layout)
- 📈 a Grafana dashboard nobody except me has ever looked at
Achievement unlocked. 🏆
We now return to the actual topic of this post.
🚀 What This Repository Does
The codebase I’m publishing today is an Ansible-based infrastructure toolkit
that provisions and manages workloads on a
single-node K3s cluster running on AlmaLinux 9:
- ☁️ Nextcloud — self-hosted file sync and collaboration,
including Collabora CODE for online office editing - 📝 WordPress — for exactly the kind of blog you’re reading right now
Both stacks share a common set of Ansible roles covering:
SSH hardening 🔐,
firewall rules 🛡️,
monitoring 📊,
mail relay,
automatic security updates,
rootkit scanning,
and audit logging.
The playbooks are designed to be idempotent:
you can run them repeatedly against a live system and they only change what actually
needs changing. ✨
The entire infrastructure is described in code.
There are no hidden manual steps buried in a notebook somewhere.
If the server burns down (figuratively — it’s a VPS 😄),
a new one can be provisioned from scratch with a single
ansible-playbook command and a restore from backup.
☸️ Architecture: Nextcloud on K3s
The core design decision is a single-node Kubernetes cluster using K3s.
No high availability. No multi-master setup. No etcd cluster.
Just one server, one node, one point of failure — intentionally.
For this kind of workload, the operational simplicity is worth far more than pretending
to run a hyperscaler in a homelab. 🙂
K3s still gives us all the good Kubernetes features:
- ⚙️ declarative manifests
- 🔄 rolling deployments
- 🔐 cert-manager
- 🌍 ingress controllers
…without the operational overhead.
🛡️ Security: Defence in Depth
The security model is layered. No single mechanism is trusted exclusively.
Instead, the stack combines multiple independent controls:
- 🔥 iptables with default DROP policies
- 🔐 hardened SSH configuration
- 🚫 Fail2Ban for brute-force protection
- ⚡ nginx rate limiting
- 🧩 SELinux in enforcing mode
- 🔑 encrypted secrets via Ansible Vault
- 🕵️ auditd + rkhunter monitoring
- 📦 automatic security updates
Or, put differently:
defence in depth, not
“I hope nobody scans this VPS.” 😄
📊 Monitoring: Prometheus + Grafana
Monitoring is fully automated using:
Prometheus 📈,
Grafana 📊,
Node Exporter,
mysqld_exporter,
and PHP-FPM metrics.
Dashboards are automatically imported and configured by the playbook,
including:
- 📈 Node Exporter Full
- 🗄️ MySQL Overview
- ⚡ PHP-FPM metrics
Because if you’re going to over-engineer a homelab,
you might as well have beautiful graphs showing exactly how over-engineered it is. 🙂
💾 Backup and Restore
The repository includes actual backup and restore scripts —
not just optimistic feelings and a folder named
backup_final_v2_REAL. 😅
Backups cover:
- 🗄️ MariaDB dumps
- 📂 Nextcloud application files
- ☁️ user data
- 🔄 restore automation
After a restore, re-running the playbook ensures all configuration and
application settings are brought back into the expected state automatically.
🚀 Getting Started
The repository targets a fresh AlmaLinux 9 installation.
The setup process is intentionally straightforward:
- ⚙️ Configure your inventory and host variables
- 🔐 Encrypt your vault with Ansible Vault
- 📦 Install the required Ansible collections
- 🚀 Run the playbook
The deployment is fully idempotent, meaning future changes —
whether image updates, firewall tweaks, or additional apps —
can simply be rolled out by running the playbook again.
📥 Download
The full repository is available as a ZIP archive.
Extract it, fill in your host_vars,
encrypt your vault, and you’re ready to deploy:
unzip www_k3s.zip
cd www_k3s
ansible-galaxy collection install -r requirements.yml
ansible-vault encrypt inventory/host_vars/<host>/vault.yml
ansible-playbook nextcloud-k3s.yml --limit <host> --ask-vault-passQuestions, issues, or suggestions? 💬
Drop them in the comments below.
Small note: I still haven’t fully sorted out how I want to publish and maintain
the code properly via GitLab yet, so for now the current state is provided as a simple
ZIP archive 📦. Long-term I may move everything to GitHub or another public repository,
but that’s still an open infrastructure debate between me, my free time, and several
unfinished TODO lists. 😄




