Back to Home

Docker Compose for IaC servers: setup

Approach uses Docker Compose for IaC personal servers: bootstrap and host-facts scripts, idempotent nftables firewall, WireGuard with cleanup. Deploy via rsync+ssh, Renovate updates. Repo template for DNS/VPN/monitoring.

IaC servers: Docker Compose up without Ansible
Advertisement 728x90

Managing Personal Servers with Docker Compose as IaC

Hands-on experience shows that handling 1–5 servers with docker compose up streamlines Infrastructure as Code (IaC) without needing Ansible. This approach requires familiarity with docker-compose.yml, manual setup experience, and a drive to automate. Key pieces include a script for rsync + SSH deployment and storing secrets in Git (using sops or git-secret). Docker Compose delivers idempotency, cutting down on manual work.

The repo with the template showcases DNS/email (primary/secondary), VPN (WireGuard), nftables firewall, and Netdata monitoring with Telegram alerts. It includes docs and infrastructure diagrams.

Initial Setup and Bootstrap

For a new server, create bootstrap.sh—a minimal script to install Docker. Run it manually or via cloud-init. The example keeps changes to a minimum: OS upgrades (like do-release-upgrade) or console tools (nvim config) need occasional tweaks.

Google AdInline article slot
# Simplified bootstrap.sh snippet
curl -fsSL https://get.docker.com | sh
systemctl enable --now docker
docker compose version

The script skips redundant manual steps, zeroing in on Docker.

Idempotent Firewall with nftables

Docker tweaks nftables rules in the ip/ip6 families. Store yours in inet to dodge conflicts. bootstrap.sh sets up /etc/nftables.conf, and the container applies migrate.nft on up.

Use dockerize to render and inject variables (like IP from host-facts.sh). Full idempotency:

Google AdInline article slot
flush ruleset inet
flush chain ip filter DOCKER-USER
flush chain ip6 filter DOCKER-USER

table inet filter {
    chain prerouting-before-docker {
        type filter hook prerouting priority dstnat - 2;
        # Filtering before Docker NAT
    }
}

Handling WireGuard and System Emails

WireGuard spins up a kernel interface: tear it down when stopping the container.

finish() {
    wg-quick down "$INTERFACE"
}
trap finish EXIT
wg-quick up "$INTERFACE"
sleep inf

System emails (from systemd, unattended-upgrades) route through a bind-mount of /var/spool/postfix into a postfix container. Shared queue between host and container.

host-facts.sh for Server-Specific Details

This script generates env vars for docker-compose.yml: IP, UID/GID, interfaces.

Google AdInline article slot
uid() { getent passwd "$1" | cut -d: -f3; }
gid() { getent group "$1" | cut -d: -f3; }
metadata() { curl -s "http://169.254.169.254/metadata/v1$1"; }

set -euo pipefail

if test -z "${DEVEL:-}"; then
    WAN_IP="$(metadata /interfaces/public/0/ipv4/address)"
    UID_POSTFIX="$(uid postfix)"
    GID_POSTFIX="$(gid postfix)"
else
    WAN_IP=0.0.0.0
    UID_POSTFIX=1001
    GID_POSTFIX=1001
fi
for _var in WAN_IP UID_POSTFIX GID_POSTFIX; do eval "export $_var=\"$${_var}\" "; done

— Editorial Team

Advertisement 728x90

Read Next