First-Time Setup & Deployment (Docker Compose)

This page walks you through deploying the application using Docker Compose on a fresh host. You’ve already pulled the image (see previous page).

Prerequisites

  • Docker Engine / CLI 28.1.1 (or compatible)
  • Docker Compose plugin v2.x
  • Image already pulled from synaccess.azurecr.io
  • A deployment folder (example path used below: /opt/syn-cloud)

Check your versions:

docker --version
docker compose version

1) Create the deployment folder

sudo mkdir -p /opt/syn-cloud/{certs}
cd /opt/syn-cloud
  • Put any TLS/CA files you need under ./certs/ (host) — they’ll appear in the container at /certs/.....

2) Create the files

Create docker-compose.yml in /opt/syn-cloud:

version: "3.9"
services:
  syn-cloud:
    # Default to 'latest' unless TAG is set in .env
    image: synaccess.azurecr.io/syn-cloud:${TAG:-latest}
    container_name: syn-cloud
    env_file:
      - .env

    # Uncomment/leave as-is to make TLS material or other read-only assets
    # available inside the container. For example, mount database CA certificates
    # or HTTPS cert/key pairs and reference the in-container paths from your .env
    # (e.g., TLS_CERT=/certs/fullchain.pem).
    volumes:
      - ./certs:/certs:ro

    # Publishes the app on the same port configured in .env.
    # When PORT=443 and CONNECTION_SECURITY=tls the container will terminate TLS.
    ports:
      - "${PORT:-80}:${PORT:-80}"

    restart: unless-stopped

    # Always check for a newer image on 'up' (useful when tracking 'latest')
    # pull_policy: always

Create .env next to it (copy/paste then edit values):

# These values are consumed by docker-compose.yml and the application at runtime.

# -----------------------------------------------------------------------------
# Database connectivity
# -----------------------------------------------------------------------------
# Full PostgreSQL connection URI. Include credentials and database name.
# Example for managed Postgres: postgres://user:password@host:5432/database
DB_CONNECTION_STRING=postgres://username:password@database-host:5432/database-name

# Connection pool sizing. Keep small for low-resource deployments.
DB_MIN_CONNECTIONS=2
DB_MAX_CONNECTIONS=40

# TLS options (optional). Set DB_SSL_MODE to one of: disable, prefer, require, verify-ca, verify-full.
# When supplying certificate paths, mount them via docker-compose (see volumes example) and
# point these variables at the in-container path (e.g. /certs/root.crt). You can also
# inline PEM content by escaping newlines with \\n 
DB_SSL_MODE=
DB_SSL_ROOT_CERT=
DB_SSL_CERT=
DB_SSL_KEY=
# Set to false ONLY when you must skip certificate validation (not recommended).
DB_SSL_REJECT_UNAUTHORIZED=

# -----------------------------------------------------------------------------
# Application runtime
# -----------------------------------------------------------------------------
# The public hostname for the deployment (used when generating links/emails).
HOST=localhost

# Listening port inside the container AND host mapping. Ensure docker-compose ports section matches.
# Common values: 80 for HTTP, 443 when running TLS directly, 3000 for alternate ports.
PORT=80

# Bind address used by the web server; :: listens on all IPv4/IPv6 addresses.
BIND_HOST=0.0.0.0

# Optional override for the externally reachable URL. Leave blank to auto-generate from HOST/PORT/CONNECTION_SECURITY.
HOST_URL=

# Choose none or tls. When tls is selected, provide TLS_CERT and TLS_KEY.
CONNECTION_SECURITY=none

# When true AND running TLS, the app will redirect HTTP (80) to HTTPS (443) inside the container.
FORWARD_PORT_80_TO_443=false

# TLS certificate and key can be either inline PEM (escape newlines with \\n) or filesystem paths mounted into the container.
TLS_CERT=
TLS_KEY=

# Comma-separated list or wildcard for allowed CORS origins. Use * for testing only.
CORS_ORIGIN=

# -----------------------------------------------------------------------------
# Image pinning (optional)
# -----------------------------------------------------------------------------
# Leave TAG unset to track 'latest'. To pin/revert to a specific version, set TAG here.
# TAG=2025.10.02

Tip: Keep a backup before edits:

cp .env .env.backup.$(date +%Y%m%d-%H%M%S)

3) Login (one-time per host)

docker login synaccess.azurecr.io -u <TOKEN_USER> -p '<TOKEN_PASSWORD>'

Expected output: Login Succeeded.


4) Start the application

docker compose up -d

Compose will create (or recreate) the container and start it with the variables from .env.


5) Verify

docker compose ps
docker compose logs --tail=200 syn-cloud
curl -I http://127.0.0.1:${PORT:-80} || true
  • You should see Up in docker compose ps.
  • Adjust the port if you changed PORT in .env.

6) Initial in-app setup (first login)

After the container is running and you can reach the URL, complete these one-time steps inside Syn Cloud.

  1. Open your Syn Cloud URL Visit https://<your-hostname> (or http://<your-hostname>:<port> if not using TLS).

  2. Go to Deployment Settings On first boot, the app should direct users to /deployment-settings.

  3. Create the Super Admin account

    • Username (or email)
    • Email address
    • Strong password
    • Save

    This Super Admin account is the primary administrator. Its email/username and password are used later when pairing PDUs to your cloud.

  4. Configure SMTP (email)

    • SMTP host/port (587 STARTTLS or 465 SMTPS)
    • Username / password (service account recommended)
    • Encryption (STARTTLS or SSL/TLS)
    • Save and Send test email

    SMTP enables email verification, password resets, and user invitations. Without SMTP, users won’t be able to complete email-based flows.

  5. Confirm deployment details On Deployment Settings, verify the displayed Deployment URL and environment details match your .env (for example HOST, PORT, and TLS settings).

When finished, continue with pairing PDUs and inviting users.

7) Database Connectivity Schemes

The app reads DB settings from .env and supports three common patterns:

ScenarioHow to configure in .envWhen to use
Username + passwordDB_CONNECTION_STRING=postgres://username:password@db-host:5432/database (leave TLS vars blank, or set DB_SSL_MODE=prefer to negotiate TLS if supported).Simplest configuration for trusted/internal networks.
Server-authenticated TLSDB_CONNECTION_STRING as above; set DB_SSL_MODE=verify-ca or verify-full; point DB_SSL_ROOT_CERT at the mounted CA (e.g., /certs/root.crt).When the DB requires TLS and you only need to validate the server certificate.
Mutual TLS (client certs)Same as above plus set DB_SSL_CERT and DB_SSL_KEY to mounted paths (e.g., /certs/client.crt, /certs/client.key).Managed services or hardened deployments that require client certificates.

Mounting certificates: Place your CA/client certificates under ./certs/ (host). The compose file maps ./certs:/certs:ro, so reference them in .env as /certs/<file>.


8) Applying Migrations

  • The backend runs migrations automatically on first startup.
  • Ensure your DB user has privileges to CREATE, ALTER, and INSERT.
  • Watch docker compose logs --tail=200 syn-cloud for migration output on first boot.

9) Configure HTTPS Termination

Choose one of the following:

  1. Terminate TLS inside the app

    • Set CONNECTION_SECURITY=tls and PORT=443 in .env.
    • Put fullchain.pem and privkey.pem under ./certs/.
    • Set in .env: TLS_CERT=/certs/fullchain.pem TLS_KEY=/certs/privkey.pem
    • (Optional) FORWARD_PORT_80_TO_443=true to redirect HTTP→HTTPS within the container.
  2. Terminate TLS at a reverse proxy (nginx, Caddy, Traefik, etc.)

    • Leave CONNECTION_SECURITY=none, keep PORT=80.
    • Point your proxy at the container’s HTTP listener.
    • Manage certificates in the proxy; the app container does not need the private key.
  3. Hybrid / Load balancer offload

    • Present certificates at the LB and forward HTTP to the container (CONNECTION_SECURITY=none).
    • Ensure LB forwards X-Forwarded-Proto so the app generates correct HTTPS links.

When certificates change, restart to apply: docker compose up -d (or --force-recreate).


10) Edit configuration later

  • Edit .env and re-apply:
docker compose up -d
# If changes don’t apply, force recreation:
docker compose up -d --force-recreate
  • Pin or roll back to a specific image tag by setting TAG in .env, then:
docker compose pull
docker compose up -d

11) Troubleshooting

  • 401 Unauthorized during pull → Re-run the login: docker login synaccess.azurecr.io -u <TOKEN_USER> -p '<TOKEN_PASSWORD>'

  • DNS/network error

    nslookup synaccess.azurecr.io
    curl -I https://synaccess.azurecr.io/v2/ || true
  • Database connection errors on first boot Verify DB_CONNECTION_STRING and any TLS variables. Confirm mounted cert paths (/certs/...) exist.


12 What persists between restarts

  • Any mapped paths in volumes: (e.g., ./certs, ./logs) persist on the host.
  • If you add a DB service in your compose later, its data can live in ./pgdata.

Do not commit .env to version control. Keep credentials safe.