- Shell 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| adguard | ||
| duin | ||
| forgejo | ||
| invoiceninja | ||
| lego | ||
| liwan | ||
| nextcloud | ||
| ntfy | ||
| openproject | ||
| portainer | ||
| postgres18 | ||
| scheduler | ||
| shepherd | ||
| socket_proxy | ||
| syncthing | ||
| traefik | ||
| uptime_kuma | ||
| vaultwarden | ||
| .gitignore | ||
| deploy.sh | ||
| README.md | ||
docker
Docker Compose stacks deployed to a Docker Swarm cluster (self-hosted homelab/small-business
infra. Each top-level directory is one independently
deployable stack: <stack>/compose.yaml + <stack>/.env. There is no application code in this
repo — it's infrastructure-as-config.
See each stack's own README.md for what it does, which secrets/env vars it needs, and any
stack-specific notes. See CLAUDE.md for the full conventions reference (networking, secrets,
volumes, deploy labels, middleware chains, etc.) used across all stacks.
Stacks
| Stack | Purpose |
|---|---|
adguard |
Network-wide DNS ad/tracker blocking |
duin |
Diun — watches running services for new image versions, notifies via ntfy |
forgejo |
Forgejo Actions runner for git.m1ch.eu (pinned to l2-back1) |
invoiceninja |
Invoice Ninja (invoicing/billing) app + MariaDB + Valkey cache |
lego |
Let's Encrypt certificate issuance/renewal (DNS-01 via Hetzner) for Traefik |
liwan |
Liwan — privacy-friendly web analytics |
nextcloud |
Nextcloud + Collabora Office + Nextcloud Talk (video/chat) |
ntfy |
ntfy — push notification server |
openproject |
OpenProject — project management / issue tracking |
portainer |
Portainer agent (manager nodes only) |
postgres18 |
Shared PostgreSQL 18 database + backups + Adminer |
scheduler |
swarm-cronjob — runs the swarm.cronjob.* labeled one-shot jobs used by other stacks |
shepherd |
Shepherd — auto-updates services labeled shepherd.autodeploy=true |
syncthing |
Syncthing — syncs a home folder across every swarm node (global mode) |
traefik |
Traefik reverse proxy, Authentik forward-auth, Sablier scale-to-zero |
uptime_kuma |
Uptime Kuma — uptime/status monitoring |
vaultwarden |
Vaultwarden — self-hosted Bitwarden-compatible password vault |
Commands
Deploy a stack
./deploy.sh <name> <stack_name>
# e.g. ./deploy.sh traefik traefik
This resolves to <name>/compose.yaml + <name>/.env, runs docker compose config to render
the merged config (stripping the name: field and normalizing published:/size:/cpus:
quoting so docker stack deploy accepts it), and pipes the result into
docker stack deploy -c - <stack_name>.
Validate a stack before deploying
docker compose -f <name>/compose.yaml --env-file <name>/.env config
Catches syntax/interpolation errors (missing env vars, bad YAML) without touching the cluster. There is no other build/lint/test step in this repo.
Inspect what's currently running
docker service ls
docker service ps <service_name>
docker service logs <service_name> --tail 100
Dump live state of everything running on the swarm
docker service ls --format '{{.Name}}' | while read -r s; do docker service inspect "$s" > "${s}.json"; done
Used for inspection/diffing, not as a source of truth. Not git-ignored — .gitignore no
longer excludes *.json, so check git status before committing anything after running this;
a stray *.json dump can otherwise end up in a commit.
Secrets, configs, and .env
*/.env, *.bak, *.tmp, and tmp/ are git-ignored (as is CLAUDE.md itself). Every stack's
.env holds real credentials, tokens, and domain-specific values — these files exist on disk
but are never committed.
Sensitive values are passed to containers as Swarm secrets (docker secret create NAME - < value_file, referenced in compose as external: true), not as plaintext env vars. Each
stack's README lists the exact secret names it expects to already exist in the swarm.
A few stacks also mount Swarm configs (docker config create NAME <file>) for larger
config blobs that aren't secret — e.g. nginx vhost configs, Traefik's static file-provider
config. The source files for these live at the repo root or in the stack directory (e.g.
nextcloud/nginx.conf, invoiceninja/nginx.conf, traefik/compose_traefik.staticconf.yaml)
and are referenced by name in the relevant README.