# The bus box: NATS JetStream and Redis, reachable by the control plane on one # host and the fleet on others. # # Run this on a machine of its own once you have more than one worker. The # whole control plane depends on it, so it should not also be the box you # restart when you redeploy a worker. # # Before the first start: # 1. point bus.example.com at this machine # 2. get a certificate for it (certbot certonly --standalone -d bus.example.com) # 3. groupadd -g 2000 warmbly-certs, the group that may read the private key # 4. install certbot-deploy-hook.sh so renewals land in /opt/warmbly/certs # 5. write .env next to this file with NATS_TOKEN and REDIS_PASSWORD # # NATS_TOKEN=$(openssl rand -hex 32) # REDIS_PASSWORD=$(openssl rand -hex 32) services: nats: image: nats:2.10-alpine restart: unless-stopped # Only the config file. Passing -js/-sd/-m as well duplicates what # nats.conf already sets, and NATS refuses to start on a duplicate # rather than picking one. command: ["-c", "/etc/nats/nats.conf"] environment: NATS_TOKEN: ${NATS_TOKEN:?set NATS_TOKEN in .env} # The private key is 0640 and owned by this group, so neither container # needs it world-readable and nothing else on the box can read it. group_add: - "${WARMBLY_CERT_GID:-2000}" volumes: - ./nats.conf:/etc/nats/nats.conf:ro - ${WARMBLY_CERT_DIR:-/opt/warmbly/certs}:/certs:ro - nats_data:/data ports: - "4222:4222" healthcheck: # 127.0.0.1, not localhost: nats.conf binds the monitor to IPv4 # loopback, and `localhost` resolves to ::1 first in a container, so the # check never reaches it and the service sits unhealthy while serving # traffic perfectly well. test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:8222/healthz"] interval: 10s timeout: 3s retries: 5 redis: image: redis:7-alpine restart: unless-stopped # Pinned so the entrypoint never runs as root. Left as root it drops to # uid 999 with gosu, which resets supplementary groups and throws away the # group_add below, and then it cannot read the private key. user: "999:999" # TLS is not optional on this box. Redis holds each organization's # decrypted data key for the life of its cache entry, so a plaintext # connection across the internet publishes key material. The password # protects access; only TLS protects the traffic. # # 6379 stays open for containers on this host and is never published; # 6380 is the port the control plane connects to. command: > redis-server --port 6379 --tls-port 6380 --tls-cert-file /certs/fullchain.pem --tls-key-file /certs/privkey.pem --tls-ca-cert-file /certs/chain.pem --tls-auth-clients no --requirepass ${REDIS_PASSWORD:?set REDIS_PASSWORD in .env} --appendonly yes --maxmemory-policy noeviction group_add: - "${WARMBLY_CERT_GID:-2000}" volumes: - ${WARMBLY_CERT_DIR:-/opt/warmbly/certs}:/certs:ro - redis_data:/data ports: - "6380:6380" healthcheck: test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] interval: 10s timeout: 3s retries: 5 volumes: nats_data: redis_data: