Skip to content

Environment variables

Copy .env.example to .env and fill in what you need. See Get started → Install for the handful that are actually required. The release stack has its own, shorter env.example (attached to every GitHub release and in deploy/release); every variable in it is described here, and the repository’s .env.example is the full list. An empty value in .env falls back to the default named here; NODE_ENV is deliberately absent from the file, since the release image always runs with NODE_ENV=production and any entry here, even an empty one, would override that.

Variable Default Purpose
PORT 3000 HTTP port of the api process.
RESTOW_PUBLIC_URL none Public origin the browser uses (origin only), e.g. https://app.example.com or http://localhost:5173.
RESTOW_APP_DOMAIN none Public app domain for the Caddy edge (automatic Let’s Encrypt TLS), e.g. app.example.com.
RESTOW_API_URL none Internal API base URL.
RESTOW_MODE none Operating mode: local | public. Set by the setup wizard; may be pinned here.
RESTOW_EDITION community Edition until a license key is installed: community | business | service_provider.
RESTOW_LICENSE_PUBLIC_KEY built into the release Optional override of the Ed25519 license verification key (raw, base64url).
RESTOW_UPDATE_CHECK_URL empty: the Updates tab decides Environment override for the update check: a releases endpoint over https (a GitHub .../releases or .../releases/latest URL, or a Forgejo or Gitea releases API). When set it wins over the tab under Settings, Updates: the check is on, the source is this URL and read-only there, and no stored token is sent to it. Empty means the tab decides, and the check is off until an administrator turns it on. Only the release list is read; no installation data is sent. See Updates.
LOG_LEVEL info Log level of worker and scheduler: debug | info | warn | error.
RESTOW_DOCS_TROUBLESHOOTING_URL https://docs.restowbackup.com/administrators/troubleshooting/ The page the failure explanations in the web interface link to for more help. Point it at your own runbook if you keep one.

RESTOW_VERSION (the running release) is written into the image at build time; do not set it in .env, since an empty value there would hide it.

Variable Default Purpose
RESTOW_IMAGE none (required in the release stack) The application image with its version, used by api, worker, scheduler and the updater, for example ghcr.io/restow-backup/restow:0.1.0. In the source stack leave it empty: Compose then runs the restow:local image it builds. The opt-in updater writes this line.
RESTOW_WEB_IMAGE none (required in the release stack) The web image with the Caddy edge, for example ghcr.io/restow-backup/restow-web:0.1.0. Empty in the source stack: restow-web:local. The updater writes this line too.
RESTOW_PROJECT_DIR /srv/restow Absolute path, on the host, of the directory that holds docker-compose.yml and .env, for example /opt/restow. Only the optional updater needs it; it mounts the directory at the same path.
RESTOW_UPDATER_URL http://updater:8090 Where the api looks for the optional updater. Nothing answers there unless the updater profile runs.

The updater (docker compose --profile updater up -d, opt-in) mounts the Docker socket, which is root on the host; read Updates first. The variables below are read by the updater container only (ROLE=updater). The updater service gets no .env, so the defaults apply unless you add a variable to that service’s environment in the Compose file.

Variable Default Purpose
RESTOW_UPDATER_PROJECT_DIR set from RESTOW_PROJECT_DIR Absolute host path of the Compose project, mounted at the same path.
RESTOW_UPDATER_IMAGE_REPOSITORY ghcr.io/restow-backup/restow Application image repository for the image mode.
RESTOW_UPDATER_WEB_IMAGE_REPOSITORY ghcr.io/restow-backup/restow-web Web image repository for the image mode.
RESTOW_UPDATER_HEALTH_TIMEOUT_SECONDS 600 How long to wait for the new api (migrations can take a while).
RESTOW_UPDATER_MIN_FREE_MB 1024 Free space required in the updater’s volume.
RESTOW_UPDATER_CLI_IMAGE docker:27-cli Image used to run docker and docker compose.
RESTOW_UPDATER_SOURCE_HOSTS any https host Optional comma-separated list of hosts a source archive may be downloaded from.
Variable Default Purpose
RESTOW_EDGE_TRUSTED_PROXIES loopback only Peers whose X-Forwarded-For the edge keeps (and appends to) instead of replacing with the peer’s own address: space-separated CIDR ranges, or private_ranges. Needed only when another reverse proxy sits in front of Caddy.
RESTOW_EDGE_HSTS false true sends Strict-Transport-Security (with includeSubDomains). Set it only once RESTOW_APP_DOMAIN is a real, publicly resolvable domain for which the edge terminates its own valid TLS certificate (public mode). The value is compared literally, so it must be exactly lowercase true; anything else leaves HSTS off. Never set it for a local or IP installation: browsers would then insist on HTTPS for a host that may never serve it.
Variable Default Purpose
POSTGRES_PASSWORD none (required) Password for the Postgres superuser Docker Compose creates. POSTGRES_USER/POSTGRES_DB default to restow.
DATABASE_MIGRATION_URL none (required) The database owner; runs migrations only.
DATABASE_URL none (required) The application role, subject to Row Level Security. The migration step creates it with the name/password given here.
DATABASE_PROVIDER_URL none (required) The installation role (BYPASSRLS), created the same way.

Three roles, three connection strings, three different passwords. See Get started → The three database roles. The processes refuse to start if DATABASE_URL turns out to be a superuser or able to bypass RLS.

Generate both with openssl rand -base64 32.

Variable Purpose
RESTOW_MASTER_KEY Required. 32-byte key-encryption key (KEK), base64. Wraps every tenant key; keep a copy offline, see Backing up Restow itself.
BETTER_AUTH_SECRET Required. Secret for the auth system’s own tokens and sessions.

Microsoft Entra (multi-tenant app for backup, client credentials)

Section titled “Microsoft Entra (multi-tenant app for backup, client credentials)”
Variable Purpose
ENTRA_CLIENT_ID The backup app’s Application (client) ID.
ENTRA_CLIENT_SECRET Client secret credential (fill exactly one of this or the certificate path).
ENTRA_CLIENT_CERT_PATH Optional: certificate path (PEM, private key + certificate) instead of a secret. Used in preference to the secret if both are set.
ENTRA_AUTHORITY_HOST Optional: authority host for sovereign clouds. Default https://login.microsoftonline.com.
DIRECTORY_FULL_SYNC_HOURS Default 24. Hours between full directory enumerations; incremental runs happen in between.

See First steps → Connect Microsoft 365 for how to obtain these.

End-user SSO (delegated OIDC via Entra common)

Section titled “End-user SSO (delegated OIDC via Entra common)”
Variable Purpose
ENTRA_SSO_CLIENT_ID Client ID of a separate app registration used only for end-user sign-in (delegated openid profile email, no data access).
ENTRA_SSO_CLIENT_SECRET Its client secret.
Variable Purpose
MAIL_TRANSPORT smtp | graph.
SMTP_HOST, SMTP_PORT SMTP server and port.
SMTP_SECURE true for implicit TLS (also assumed on port 465); otherwise STARTTLS.
SMTP_USER, SMTP_PASSWORD Optional SMTP credentials.
SMTP_FROM Sender address.
GRAPH_MAIL_SENDER Sender mailbox for the Microsoft Graph sendMail transport.
GRAPH_MAIL_TENANT_ID Entra tenant of the sender mailbox, if the setup wizard names none.
Variable Default Purpose
IMAP_ALLOW_INSECURE false Permits IMAP sources without TLS. Development only; never in production.
IMAP_ALLOW_PRIVATE_NETWORKS provider-saved only true lets every IMAP source reach loopback and private networks; by default only servers a provider admin saved can, and tenant admins are limited to public servers.
Variable Default Purpose
STORAGE_TARGET local Target type: local | s3.
STORAGE_LOCAL_PATH /data/chunks Path inside the container for the default local target.
STORAGE_COPY_LOCAL_PATH none Optional second target every default write is copied to (a mounted NFS/SMB share).
S3_ENDPOINT, S3_REGION, S3_BUCKET, S3_PREFIX none S3-compatible target (Hetzner Object Storage, Garage, Wasabi, B2, AWS, …).
S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY none S3 credentials.
S3_FORCE_PATH_STYLE true false for virtual-hosted bucket addressing.

All optional. See Mail file import and export.

Variable Default Purpose
RESTOW_IMPORT_DIR ./import next to docker-compose.yml Host directory that is mounted read-only at /var/lib/restow/import in the api and the worker. Each tenant uses its own subfolder <directory>/<tenant slug>/, which you create yourself.
IMPORT_DIR /var/lib/restow/import The path inside the containers. Change it only together with the mount above.
IMPORT_MAX_FILE_BYTES 10 GiB Largest file the browser upload accepts, in bytes.
IMPORT_UPLOAD_TTL_HOURS 48 Hours an unfinished or unused upload is kept before it is deleted.
IMPORT_SEGMENT_BYTES 8 MiB Size of one upload chunk in bytes (allowed: 64 KiB to 32 MiB).
IMPORT_MAX_MESSAGE_BYTES 256 MiB Largest single message that is read into memory.
EXPORT_TTL_HOURS 24 Hours a finished export can be downloaded before its file is deleted.

All optional. The defaults suit the release stack; only a custom image needs changes. See Endpoint backup.

Variable Default Purpose
RESTIC_BINARY restic from the PATH The restic binary that the api and the worker run for retention, checks, restore tests, browsing and downloads. The image ships it in /usr/local/bin/restic.
RESTOW_RESTIC_CACHE_DIR the temp folder Where restic keeps its per-endpoint caches. A folder on a volume, for example /data/restic-cache, keeps them across restarts and makes browsing faster.
RESTOW_AGENT_DIR /srv/agent Where the image keeps the agent binaries.
RESTOW_AGENT_INSTALL_DIR /srv/agent/install Where the image keeps the install scripts.
Variable Default Purpose
WORKER_CONCURRENCY 2 Parallel jobs per queue in one worker process.
WORKER_TENANT_CONCURRENCY 2 Parallel jobs per tenant in one worker process.
WORKER_POLL_SECONDS 2 pg-boss polling interval.
WORKER_SHUTDOWN_TIMEOUT_MS 30000 How long shutdown waits for running jobs to checkpoint.
WORKER_CANCEL_POLL_MS 15000 How often a running job checks whether it was cancelled.
WORKER_PROGRESS_FLUSH_ITEMS 50 Progress is written every N items…
WORKER_PROGRESS_FLUSH_MS 2000 …or at least every M milliseconds.
Variable Default Purpose
WEBHOOK_POLL_MS 5000 Poll interval for due deliveries.
WEBHOOK_CONCURRENCY 4 Parallel deliveries.
WEBHOOK_TIMEOUT_MS 10000 Per-attempt timeout.
RESTOW_WEBHOOK_ALLOW_PRIVATE refused true permits webhook targets in loopback and private networks.
Variable Default Purpose
SCHEDULER_TICK_MS 30000 Tick interval.
SCHEDULER_LEADER_RETRY_MS 15000 Leader retry.
SCHEDULER_BATCH_SIZE 200 Due schedules per tick.
SCHEDULER_DEFER_MS 3600000 Delay before a schedule that can’t be planned (invalid cron) is looked at again.
SCHEDULER_LOCK_KEY none Postgres advisory lock key for leader election; change only when two installations share a database.

All optional; the receiver is off until JOURNAL_SMTP_PORT is set. Setup, DNS, port 25 and the certificate: Exchange journaling.

Variable Default Purpose
JOURNAL_SMTP_PORT none (receiver off) Port the archive’s SMTP journal receiver listens on. The receiver starts only when this is set. Exchange Online delivers to port 25. The repository’s .env.example sets 25; the release env.example leaves it empty.
JOURNAL_SMTP_BIND 127.0.0.1 Release stack only: the host address the port is published on. Set it to 0.0.0.0 together with JOURNAL_SMTP_PORT=25 to receive Exchange Online journal reports, which must reach the receiver directly, not through Caddy.
JOURNAL_HOSTNAME none The host name Exchange Online delivers to, for example archive.example.com: host name only, without scheme, path, port or mail address. It forms the tenants’ journal addresses, journal+<token>@<host>; publish it in DNS. Without a valid value Restow shows no complete address.
JOURNAL_TLS_CERT_PATH none Path to the certificate chain (PEM, leaf first) of a publicly trusted authority, matching JOURNAL_HOSTNAME. Exchange Online requires TLS, so the receiver does not start without a certificate and never falls back to a built-in one. In the release stack: /etc/restow/journal-tls/fullchain.pem.
JOURNAL_TLS_KEY_PATH none Path to the private key (PEM, not encrypted with a passphrase) that belongs to the certificate. Both paths must be set. In the release stack: /etc/restow/journal-tls/privkey.pem.
JOURNAL_TLS_DIR ./journal-tls Both stacks’ Compose file: the host directory that is mounted read-only into the api at /etc/restow/journal-tls. Put fullchain.pem and privkey.pem there. Renewed files are picked up within five minutes, without a restart.
JOURNAL_ALLOW_INSECURE empty (off) true starts the receiver without a certificate and without offering STARTTLS. Local development only, never in production: Exchange Online does not deliver to such a receiver and mail would cross the network in plain text. Ignored while a certificate is configured.
JOURNAL_MAX_SIZE_MB 150 Largest journal report accepted, in MB. Larger reports are refused.