Docker Deployment
Synclet ships as a single container image. The fastest way to get running is Docker Compose, which bundles PostgreSQL alongside Synclet. You can also run the container standalone or skip Docker entirely with a bare binary.
Docker Compose (Recommended)
Section titled “Docker Compose (Recommended)”Docker Compose starts PostgreSQL 16, runs database migrations, and starts Synclet in one command.
git clone https://github.com/synclet-io/synclet.gitcd syncletdocker compose up -dThe default docker-compose.yml includes four services:
postgres— PostgreSQL 16, data stored in a named volume, published on127.0.0.1:5437.migrate— Runssynclet migrate uponce before Synclet starts and exits.local-root— Makes/tmp/synclet_localon the host world-writable (mode1777) once and exits, so connectors running as any user can write there.synclet— API, scheduler, workers and the Docker executor in a single container (started withserver --standalone), published on127.0.0.1:8080.
The synclet service runs as user: "0:0": root reaches the Docker socket whatever group owns it on the host, and can write to /tmp/synclet and /tmp/synclet_local, which the Docker daemon creates root-owned. Access to the Docker socket is root-equivalent on the host either way.
Once the containers are healthy, open the dashboard at http://localhost:8080.
Customizing the Compose file
Section titled “Customizing the Compose file”| Variable | Purpose | Default |
|---|---|---|
DB_DSN | PostgreSQL connection string | Set by Compose |
AUTH_JWT_SECRET | Token signing key (min. 32 characters) | Public default in .env.compose; override in .env |
SECRET_ENCRYPTION_KEY | Base64-encoded 32-byte AES-256 key for credentials | Public default in .env.compose; override in .env |
PUBLIC_HTTP_SERVER_ADDR | Public HTTP listen address | 0.0.0.0:8080 |
INTERNAL_HTTP_SERVER_ADDR | Internal HTTP listen address (executor API, /live, /ready, /metrics) | 0.0.0.0:8081 |
DOCKER_EXECUTOR_TEMP_DIR_ROOT | Per-task scratch dir (config / catalog / state) under which connector bind mounts are created. Required when Synclet itself runs in a container with the host docker.sock mounted — see note below. | /tmp/synclet |
DOCKER_EXECUTOR_LOCAL_ROOT | Host directory mounted at /local in every connector container and never cleaned: file-writing destinations (DuckDB, CSV, local JSON, SQLite) leave their files here. Like the temp dir, it must be bind-mounted at the same path on the host. | /tmp/synclet_local |
Generate production secrets before first start:
# AUTH_JWT_SECRETopenssl rand -base64 32
# SECRET_ENCRYPTION_KEY (must be exactly 32 bytes, base64-encoded)openssl rand -base64 32Docker Standalone
Section titled “Docker Standalone”If you already have a PostgreSQL 16 instance, run the published image ghcr.io/synclet-io/synclet:<version> on its own (releases are listed on GitHub Releases).
docker run -d \ --name synclet \ --user 0:0 \ -p 8080:8080 \ -e DB_DSN="postgres://synclet:password@host.docker.internal:5432/synclet?sslmode=disable" \ -e AUTH_JWT_SECRET="$(openssl rand -base64 32)" \ -e SECRET_ENCRYPTION_KEY="$(openssl rand -base64 32)" \ -e DOCKER_EXECUTOR_TEMP_DIR_ROOT="/tmp/synclet" \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /tmp/synclet:/tmp/synclet \ ghcr.io/synclet-io/synclet:0.12.0 server --standaloneWhy the docker.sock and the temp dir? Synclet needs access to the Docker socket (
/var/run/docker.sock) to launch Airbyte connector containers during syncs. The image runs as a non-root user by default;--user 0:0lets it reach the socket whatever group owns it on the host.When Synclet itself runs inside a container and talks to the host daemon via that socket, every per-task scratch file (the rendered
config.json,catalog.json, andstate.json) is created by Synclet on the container’s filesystem, but the daemon that mounts it into the connector container is on the host.DOCKER_EXECUTOR_TEMP_DIR_ROOTis the path inside Synclet’s container where those files are written — it must be bind-mounted from an identically-named path on the host (e.g. both/tmp/synclet) so the host daemon can resolve the bind-mount source.Running the binary natively on the host? Leave
DOCKER_EXECUTOR_TEMP_DIR_ROOTunset — Synclet falls back to the OS temp dir, which the host daemon can already see.Run
synclet migrate uponce against the same database before starting the server (or front it with amigratesidecar — see the bundled compose file).
Verify the container is running:
docker logs syncletBinary (No Docker)
Section titled “Binary (No Docker)”You can run Synclet as a plain binary without Docker.
- Download the latest release from GitHub Releases.
- Set the required environment variables (see Environment Variables).
- Run migrations and start the server:
export DB_DSN="postgres://synclet:password@localhost:5432/synclet?sslmode=disable"export ENVIRONMENT=production
# Required outside local development; generate each with: openssl rand -base64 32export AUTH_JWT_SECRET="your-jwt-secret"export SECRET_ENCRYPTION_KEY="your-encryption-key"
synclet migrate upsynclet server --standaloneThe dashboard is available at http://localhost:8080.
Important: Connectors still require Docker on the host: every connector, Synclet’s own Go connectors included, runs as a Docker container.
Connector resources
Section titled “Connector resources”The Docker executor sets the resources of every connector container it starts: the source and destination of a sync and the containers of check, spec and discover. A source’s or destination’s runtime_config sets them, and each key it leaves out takes the instance default.
runtime_config key | Docker setting | When unset |
|---|---|---|
memory_limit | --memory | 2Gi |
cpu_limit | --cpus (NanoCPUs) | 1 core |
memory_request | --memory-reservation, capped at the memory limit | no reservation |
cpu_request | --cpu-shares, 1024 per core | no shares set (Docker’s default weight) |
Requests on Docker are weaker than on Kubernetes. Docker does not schedule by them or refuse a container that does not fit: the memory reservation is a soft limit the kernel reclaims down to when the host runs short of memory, and CPU shares are a relative weight that only matters while containers compete for CPU.
runtime_config also takes docker_network, the Docker network the connector containers join. It cannot mount host paths: a connector reaches host files only under /local, mounted from DOCKER_EXECUTOR_LOCAL_ROOT (see files written by destinations).
The instance defaults come from PIPELINE_RUNTIME_CPU_REQUEST, PIPELINE_RUNTIME_CPU_LIMIT, PIPELINE_RUNTIME_MEMORY_REQUEST and PIPELINE_RUNTIME_MEMORY_LIMIT (see Environment Variables).
Health Checks
Section titled “Health Checks”Synclet serves /live and /ready on the internal port (8081 by default) for load balancers and orchestrators:
curl http://localhost:8081/ready# Returns HTTP 200 when the database, background jobs and event router are upThe default Synclet image is based on distroless and does not include curl or wget. To add Docker health checks, use a multi-stage build that includes a health-check binary, or check from outside the container:
# Health check from the host or a sidecarcurl -f http://localhost:8081/readyMonitoring
Section titled “Monitoring”- Dashboard — The built-in web UI at
http://localhost:8080shows pipeline status, job history, and logs. - Prometheus metrics — Synclet exposes a
/metricsendpoint compatible with Prometheus on the internal port (8081). See Production Checklist for scraping configuration.
Backup & Restore
Section titled “Backup & Restore”Database backup
Section titled “Database backup”Back up your PostgreSQL database regularly:
pg_dump -Fc "$DB_DSN" > synclet_$(date +%Y%m%d).dump
# Restorepg_restore -d "$DB_DSN" synclet_20260401.dumpKeep SECRET_ENCRYPTION_KEY with every backup: without it the credentials in the dump cannot be decrypted. See Backup and Restore for the full procedure.
Airbyte is a trademark of Airbyte, Inc. Synclet is not affiliated with, endorsed by or sponsored by Airbyte, Inc. Connector images keep their own licenses: see Connector licenses.