Skip to main content

Remote Deploy

Remote Deploy — Remote deploy is now phase-based and registry-first. ⬆ índice

Remote deploy is now phase-based and registry-first.

Model

The server is treated as one node with three logical layers:

  • core
    • Traefik
    • private Docker registry
  • system
    • PostgreSQL
    • Redis
    • Zitadel
  • apps
    • all remaining deployable services

The default transport is:

  1. build locally
  2. push locally-built images to the server registry
  3. pull exact tags on the server
  4. run docker compose up -d --no-build

Remote image builds are no longer part of normal operation.

Commands

Fresh server:

./infra init remote <host> all

Phase deploys:

./infra remote deploy <host> core
./infra remote deploy <host> system
./infra remote deploy <host> apps
./infra remote deploy <host> <project>
./infra release <host> <project>

Notes

  • core must be deployed before system or apps on a fresh server.
  • Local/Vagrant targets use the same flow, but with local short hostnames and HTTP routing by default.
  • For later application updates, prefer ./infra release <host> <project> when the project image changed.
  • Heavy project .env defaults now ship an 8GB-host memory profile for ThingsBoard, Kafka, PostgreSQL, Chatwoot, Qdrant, Airflow, n8n/Evolution, and VoxBox.
  • Registry credentials live in .env.secrets:
    • GLOBAL_CORE_REGISTRY_USERNAME
    • GLOBAL_CORE_REGISTRY_PASSWORD
  • Zitadel bootstrap is automated through security/zitadel/.runtime/management.pat plus deployment/scripts/python/zitadel_bootstrap.py.
  • Registry access is loopback-only on the server. Use an SSH tunnel from the operator machine:

8GB Host Memory Validation

After a remote deploy or release, validate real memory usage on the server instead of assuming the caps are enough:

ssh -p 36987 externaladmin@<host> "docker stats --no-stream"
ssh -p 36987 externaladmin@<host> "free -h"
ssh -p 36987 externaladmin@<host> "journalctl -k | grep -i oom || true"
ssh -p 36987 externaladmin@<host> "swapon --show"

Expected steady state for the 8GB profile:

  • stack RAM stays below 7GiB in idle/steady state
  • swap does not grow continuously during normal operation
  • no OOMKilled containers or memory-driven restart loops

Operator prerequisites

On the operator machine:

  • Docker
  • ansible
  • sshpass if using password auth
  • nmap
  • skopeo