Running Nanosync with Docker

The nanosync Docker image packages the single binary with no external runtime dependencies. It’s the same binary as the direct install — Docker just controls the environment.


Pull the image

docker pull ghcr.io/nanosyncorg/nanosync:latest

Pin to a specific version in production:

docker pull ghcr.io/nanosyncorg/nanosync:0.9.2

Available tags: latest (latest stable), edge (main branch tip), and version tags like 0.9.2. Check GitHub Container Registry for the full list.


Quick stream — no config file

Run a one-shot stream to stdout without mounting any config:

docker run --rm ghcr.io/nanosyncorg/nanosync:latest \
  stream \
  --source "postgres://user:pass@host.docker.internal:5432/mydb" \
  --tables "public.orders"

Use host.docker.internal to reach a database running on the host machine. On Linux, pass --add-host=host.docker.internal:host-gateway if your Docker version doesn’t resolve it automatically.


With config files

Mount a directory containing your YAML files:

docker run --rm \
  -v $(pwd)/config:/config \
  -e PG_PASSWORD=secret \
  -p 7600:7600 \
  ghcr.io/nanosyncorg/nanosync:latest \
  start --config /config/server.yaml

The container exposes port 7600 for the REST API and web UI. All nanosync commands that accept --config or --file take paths inside the container — mount your config directory to a predictable path like /config.

Secrets stay out of the image: pass them as environment variables with -e and reference them in YAML using ${env:VAR_NAME}.


Docker Compose for local development

A complete local setup with nanosync and a Postgres database configured for logical replication:

services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: dev
      POSTGRES_PASSWORD: dev
    ports:
      - "5432:5432"
    command: postgres -c wal_level=logical

  nanosync:
    image: ghcr.io/nanosyncorg/nanosync:latest
    depends_on:
      - postgres
    volumes:
      - ./config:/config
      - nanosync-data:/var/lib/nanosync
    environment:
      PG_PASSWORD: dev
    ports:
      - "7600:7600"
    command: start --config /config/server.yaml

volumes:
  nanosync-data:

Start both services:

docker compose up

The wal_level=logical flag in the Postgres command is required for CDC. The named volume nanosync-data persists state between container restarts — without it, nanosync loses its checkpoint on every docker compose down.

In a separate terminal, apply your pipeline definitions:

nanosync apply --host localhost:7600 --file pipelines.yaml

Or, if you don’t have nanosync installed locally, run it through the same container:

docker compose exec nanosync \
  nanosync apply --file /config/pipelines.yaml

Applying pipelines to a running container

Use --host to point the CLI at a remote or containerized nanosync server:

nanosync apply --host localhost:7600 --file pipelines.yaml

All other commands that interact with a running server accept --host the same way:

nanosync monitor --host localhost:7600
nanosync pipeline status --host localhost:7600 orders-pipeline
nanosync pipeline resume --host localhost:7600 orders-pipeline

Production image considerations

In production, avoid latest — pin to an explicit version tag so deploys are reproducible. Use a multi-stage build or a separate image pull step in your CI pipeline to bake the image into your deployment artifact before promoting it.

The container runs as a non-root user by default. If you mount host directories, ensure the directory is owned by UID 65532 (the nonroot user inside the image), or use a named volume instead.

Use a named Docker volume for the nanosync data directory (/var/lib/nanosync) in production. Without it, the state store (SQLite checkpoints, schema history) is lost on container restart and nanosync will re-snapshot all tables from scratch on the next start.