# Management plane: portainer, vault, github-runner, watchtower. # These manage / underpin the application stacks rather than being part of them, # so they are deployed by raw `docker compose` (NOT a Portainer-managed stack) — # Portainer cannot manage the stack that contains Portainer itself. # # This file owns the shared infrastructure networks: # - edge : the single reverse-proxy network. Caddy + every service it # proxies join this. Replaces caddy's old per-stack network # membership. App stacks reference it as external. # - portainer_proxy : legacy net for caddy -> portainer. Kept until the monolith # is fully dismantled, then dropped (portainer is on edge too). # # Vault lives here (moved out of the dev stack): it is estate-wide secrets infra, # must boot before the app stacks, and should not reseal when a dev tool changes. # Vault data is the /config/vault bind mount — moving the service definition here # does not touch any data. Re-unseal after (apply-compose.ps1 -VaultUnseal). # # Fresh-install order: # docker compose -f portainer-compose.yml up -d # networks + mgmt plane first # (then create the Portainer git stacks for proxy + each app domain) services: portainer: container_name: portainer image: portainer/portainer-ce:latest restart: unless-stopped networks: - portainer_proxy - edge volumes: - /var/run/docker.sock:/var/run/docker.sock - /config/portainer:/data ports: - "9100:9000" labels: - "com.centurylabs.watchtower.enable=false" # ============================================================ # VAULT (estate-wide secret management) # One-time setup after first start: # docker exec -it vault vault operator init # vault operator unseal (3x with unseal keys) # Unseal keys + root token live in .env (they are the one secret Vault # itself cannot hold). Re-unseal after a restart with: # powershell -File apply-compose.ps1 -VaultUnseal # ============================================================ vault: container_name: vault image: hashicorp/vault:latest restart: unless-stopped networks: - edge cap_add: - IPC_LOCK # Newer vault:latest mlocks more than docker's default 64KB memlock ulimit; # without this it crash-loops with "Failed to lock memory: cannot allocate # memory" despite IPC_LOCK being granted. ulimits: memlock: soft: -1 hard: -1 environment: - VAULT_ADDR=http://0.0.0.0:8200 volumes: - /config/vault/config:/vault/config:ro - /config/vault/data:/vault/data - /config/vault/logs:/vault/logs command: vault server -config=/vault/config/vault.hcl ports: - "8200:8200" labels: - "com.centurylabs.watchtower.enable=false" github-runner: container_name: github_runner image: myoung34/github-runner:latest restart: unless-stopped environment: - ACCESS_TOKEN=${GITHUB_RUNNER_ACCESS_TOKEN} - REPO_URL=https://github.com/ginnoir/homelabstack - RUNNER_NAME=valhalla - RUNNER_LABELS=self-hosted,Linux,x64 - RUNNER_SCOPE=repo - DOCKER_GROUP_GID=1001 volumes: - /var/run/docker.sock:/var/run/docker.sock - /home/ginnoir/valhalla-lab:/valhalla-lab - /config/caddy:/config/caddy labels: - "com.centurylabs.watchtower.enable=false" watchtower: container_name: watchtower image: containrrr/watchtower:latest restart: unless-stopped environment: - DOCKER_API_VERSION=1.40 - WATCHTOWER_NOTIFICATION_SKIP_TITLE=true - WATCHTOWER_NOTIFICATION_URL=ntfy://ntfy.ginnoir.com/watchtower?title=WatchtowerUpdates volumes: - /var/run/docker.sock:/var/run/docker.sock labels: - "com.centurylabs.watchtower.enable=false" networks: edge: name: edge driver: bridge portainer_proxy: name: portainer_proxy driver: bridge