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:
- build locally
- push locally-built images to the server registry
- pull exact tags on the server
- 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
coremust be deployed beforesystemorappson 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
.envdefaults 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_USERNAMEGLOBAL_CORE_REGISTRY_PASSWORD
- Zitadel bootstrap is automated through
security/zitadel/.runtime/management.patplusdeployment/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
7GiBin idle/steady state - swap does not grow continuously during normal operation
- no
OOMKilledcontainers or memory-driven restart loops
Operator prerequisites
On the operator machine:
- Docker
ansiblesshpassif using password authnmapskopeo