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-pass
π
Download www_k3s.zip
Questions, 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. π
