Skip to content
Documentation menu · version 0.1.0-alpha.4

Single-host installation (Docker Compose)

Availability: the container images for this method are published with the 1.0 release. Until then, install on Ubuntu with the native bundle (no Docker needed).

The yarma-single-host-X.Y.Z.tar.gz release bundle (source: deployments/single-host/) runs all of YARMA on one Linux server: PostgreSQL 17, Redis, NATS JetStream, SeaweedFS (S3), ClamAV, coturn, the API, the console and Caddy for public TLS. The quick start is the short version of this page.

Prerequisites

  • Linux x86_64 or arm64 with Docker Engine 24+ and the Compose plugin, plus openssl.
  • See sizing and network requirements.
  • A DNS name for the console. Agents can use the same name or a separate one (--agents-host), e.g. when TCP 18443 is reached through a different address.
  • The license issuer's public key, license.pub (licensing).

Install

sudo tar -C /opt -xzf yarma-single-host-X.Y.Z.tar.gz
cd /opt/yarma-single-host-X.Y.Z
sudo ./init.sh --domain yarma.example.com --license-pub /path/to/license.pub \
  --acme-email [email protected]
sudo docker compose up -d

init.sh refuses to run twice. Its options:

Option Default Meaning
--domain HOST required public name of the console
--version X.Y.Z the bundle's version image tag to run
--agents-host HOST --domain name in the agent-service certificate; agents must connect with this name
--license-pub FILE none license issuer public key; without it no license can be installed and enrollment stays blocked
--acme-email EMAIL none Let's Encrypt contact
--tls-cert FILE --tls-key FILE Let's Encrypt use your own certificate (PEM, full chain)
--tls-internal off self-signed console certificate; for labs only
--turn-ip IP none public IP to advertise when the server is behind NAT
--https-port, --http-port, --agent-port, --turn-port 443, 80, 18443, 3478 published ports
--registry REG ghcr.io/javadmohebbi image registry, e.g. an internal mirror
--subnet CIDR 172.30.80.0/24 internal Docker network; change it if it overlaps your LAN

Let's Encrypt needs TCP 80 and 443 reachable from the internet. For an internal-only server, use --tls-cert/--tls-key with a certificate from your internal CA.

What init.sh creates

Path Contents
.env Compose settings: version, registry, ports, database and Redis passwords
config/config.yaml API configuration (reference)
config/Caddyfile public TLS and reverse proxy
secrets/api.env API secrets: database, Redis, cookie key, TURN secret, file-encryption master key, S3 keys
secrets/keys/signing.key ECDSA P-256 key that signs access tokens
secrets/pki/ agent CA and agent-service certificate (agent-ca.crt goes to devices)
secrets/internal-ca/, secrets/api/, secrets/postgres/ private CA and certificates for TLS between the containers
secrets/license/license.pub license issuer public key
secrets/s3/, secrets/turn/ object-store credentials, coturn configuration

Files are owned by the user IDs that read them inside the containers and are not readable by other host users. Back up secrets/ and .env: losing the file master key makes stored files and recordings unreadable, and losing the agent CA means re-enrolling every device.

Security properties

  • Only Caddy (80/443), the agent service (18443) and coturn are published. PostgreSQL, Redis, NATS, the object store, ClamAV and the API's HTTPS port stay on the internal network.
  • The API runs read-only, as a non-root user, with no Linux capabilities. It requires TLS, and PostgreSQL connections use verify-full against the internal CA.
  • The console verifies the API's certificate. The API trusts X-Forwarded-For only from the internal subnet.
  • Agents authenticate with per-device certificates from the agent CA (mutual TLS) and trust only that CA, not public roots.
  • coturn refuses to relay to private, loopback and link-local addresses.

Create the first organization

sudo docker compose exec -e YARMA_BOOTSTRAP_ADMIN_PASSWORD='<password>' api \
  /yarma-api -config-dir /etc/yarma bootstrap -tenant acme -name "Acme Corp" -admin-email [email protected]

Without YARMA_BOOTSTRAP_ADMIN_PASSWORD, a password is generated and printed once. For OIDC sign-in from the start, add -issuer https://idp.example.com/realms/x -client-id yarma -client-secret-ref env:YARMA_IDP_SECRET (and put YARMA_IDP_SECRET=… in secrets/api.env). Continue with first-time setup.

Operations

Task Command (in the bundle directory)
Status docker compose ps
Logs docker compose logs -f api (JSON lines; also web, caddy, postgres, …)
Readiness curl https://yarma.example.com/api/readyz
Metrics Prometheus format on port 19091 of the api container; attach Prometheus to the yarma-server_yarma network to scrape it
Restart docker compose restart api
Stop / start docker compose stop / docker compose up -d
Audit chain check docker compose exec api /yarma-api -config-dir /etc/yarma verify

Configuration

Edit config/config.yaml, then docker compose up -d --force-recreate api. Any key can also be set as an environment variable in secrets/api.env: files.max_bytes becomes YARMA_FILES_MAX_BYTES. Common settings:

Key Purpose
files.max_bytes, files.recording_retention_days, files.require_scan file transfer limit (default 100 MiB), recording retention (90 days), refuse files when ClamAV is down (on)
notify.smtp_addr, notify.smtp_from, notify.smtp_username (+ YARMA_NOTIFY_SMTP_PASSWORD) email notifications
pam.* credential vaults (privileged access)
auth.block_private_directories stop tenant-configured identity providers from reaching private addresses (set it for MSP or multi-customer servers)
license.center_url License Center for online activation
license.server_token (YARMA_LICENSE_SERVER_TOKEN), license.report_interval only on the License Center operator's own cloud servers: report health and usage and receive provisioning jobs (default every 5m)
agents.public_url, agents.downloads_dir address devices connect to (default https://<console host>:18443); agent packages offered for download
turn.urls TURN URLs handed to browsers and agents; add turns: if you terminate TLS for TURN elsewhere
telemetry.mode, telemetry.url anonymous usage statistics: ask (default, an administrator decides), on, off (details)
log.level debug, info, warn, error

The full list of keys, with defaults and validation, is in backend/internal/config/config.go. In production the API refuses to start without TLS, a signing key, a cookie key, a TURN secret, a file master key or with database TLS disabled.

TURN behind NAT

If the server has a private address and the public IP is NATed to it, run init.sh --turn-ip <public IP>. On an existing install, add external-ip=<public IP> to secrets/turn/turnserver.conf with root, then docker compose restart coturn. Every concurrent session can use relay ports; widen min-port/max-port there (and in the firewall) for more than about 20 simultaneous relayed sessions.

Upgrade

  1. Read the release notes and run backup.sh (backup).
  2. Set the new version in .env (YARMA_VERSION=X.Y.Z).
  3. sudo docker compose pull && sudo docker compose up -d

Database migrations run automatically (the migrate service) before the API starts; a failed migration leaves the old API stopped and is shown by docker compose logs migrate. Agents keep running during the upgrade and reconnect when the API is back. To roll back, restore the database backup taken before the upgrade and set the previous version.

Backup and restore

backup.sh writes the database dump, the object store (files, recordings) and .env, config/ and secrets/ into a time-stamped directory with a SHA256SUMS file:

cd /opt/yarma-single-host-X.Y.Z
sudo [email protected] RETENTION_DAYS=30 ./backup.sh /var/backups/yarma

secrets.tgz holds every key of the installation: always set BACKUP_GPG_RECIPIENT (or encrypt the copies yourself) and keep the backups off the server. Schedule it daily, e.g. /etc/cron.d/yarma-backup:

30 2 * * * root cd /opt/yarma-single-host-X.Y.Z && [email protected] ./backup.sh /var/backups/yarma >/dev/null

To restore (same server after a loss, or a new server), unpack the bundle of the same version and run restore.sh instead of init.sh:

sudo tar -C /opt -xzf yarma-single-host-X.Y.Z.tar.gz && cd /opt/yarma-single-host-X.Y.Z
sudo ./restore.sh /var/backups/yarma/yarma-<stamp>       # gpg must be able to decrypt

It checks the checksums, restores secrets and configuration (unless .env already exists), the database into an empty PostgreSQL and the object store, starts everything and runs verify, which fails if the schema is not current or an audit hash chain is broken. It refuses to overwrite a database that already has data. Keep the same DNS names: enrolled agents then reconnect with their existing certificates. See also backup and DR.

Uninstall

cd /opt/yarma-single-host-X.Y.Z
sudo docker compose down          # stop and remove containers; data volumes are kept
sudo docker compose down -v       # also delete all data (database, files, recordings)
sudo rm -r /opt/yarma-single-host-X.Y.Z   # configuration and keys

Uninstall the agents first if the server is going away for good (agents), otherwise they keep retrying the connection.

Documentation for Yarma 0.1.0-alpha.4. Something unclear? .