NubleStation

Stack overview

NubleStation runs as a single Docker Compose stack in infra/docker-compose.yml. Every service is a separate container, communicating over the internal nuble Docker bridge network.

# High-level structure
services:
  caddy        # reverse proxy + static files — ports 80, 443 published to host
  coredns      # LAN DNS — port 53 published to host
  api          # Gateway — API entry point, internal only, Caddy forwards to it
  blaze        # Database service — internal only
  vault        # File storage service — internal only
  orbit        # Deploy service — internal only
  console      # Next.js admin dashboard — internal only, Caddy forwards to it
  identity     # Auth service — sessions, shared-cookie SSO
  postgres     # PostgreSQL 16 — internal only

networks:
  nuble:       # internal bridge — services communicate here

volumes:
  postgres-data:   # Postgres data directory (persistent)
  nuble-apps:      # deployed frontend files (/var/nuble/apps/)
  nuble-storage:   # Vault file storage (/var/nuble/storage/)
  caddy-data:      # Caddy TLS certificates and state

Published ports

Only two ports are published to the host network interface (and therefore to the LAN):

PortServicePurpose
53/udp, 53/tcpCoreDNSLAN DNS resolution
80/tcpCaddyHTTP (redirects to HTTPS)
443/tcpCaddyHTTPS (with internal CA cert)

Every other service listens only on the internal nuble network. No other port is reachable from the LAN.

Service dependencies

caddy
  └── depends_on: gateway, console

api (gateway)
  └── depends_on: postgres, blaze, vault

blaze, vault, orbit
  └── depends_on: postgres

postgres
  └── (no dependencies — starts first)

redis
  └── (no dependencies — starts first)

coredns
  └── (no dependencies — starts first)

Health checks

Every service declares a health check. Docker marks it healthy only when the check passes. Compose’s condition: service_healthy ensures dependent services wait:

gateway:
  depends_on:
    postgres:
      condition: service_healthy
    redis:
      condition: service_healthy
# Check all services
docker compose -f infra/docker-compose.yml ps

# Tail logs for a specific service
docker compose -f infra/docker-compose.yml logs db --follow

# Restart a single service
docker compose -f infra/docker-compose.yml restart gateway

Volumes

VolumeMounted atContents
postgres-data/var/lib/postgresql/dataPostgres data files
nuble-files/var/nuble/Deployed frontend bundles + uploaded files
caddy-data/dataCaddy TLS state (certificates)

postgres-data is persistent — it survives container restarts and upgrades. Removing it with down -v erases all data.

Environment variables

All secrets are in infra/.env, generated by scripts/install.sh. The Compose file reads them with env_file: .env.

Key variables:

VariableDescription
ORG_NAMEOrganization name — used as subdomain root
HOST_IPLAN IP of the host machine
POSTGRES_PASSWORDGenerated at install time
INTERNAL_HMAC_SECRETShared HMAC secret across gateway + all services
REDIS_PASSWORDGenerated at install time

Auto-restart policy

All services use restart: unless-stopped. If a container crashes, Docker restarts it automatically. If the host machine reboots, Docker starts the stack if Docker is configured to start on boot (systemctl enable docker).

Upgrading

To upgrade NubleStation services to a newer version:

# Pull new images
docker compose -f infra/docker-compose.yml pull

# Recreate containers with new images (zero-downtime for stateless services)
docker compose -f infra/docker-compose.yml up -d --remove-orphans

Platform migrations run automatically at DB service boot — the service applies any pending schema changes before accepting traffic.