Docker and Compose
The official image is small (a distroless base with only the static binary), runs as a non-root user and keeps all state in the /data volume.
Image and tags
Images are published to ghcr.io/ja7ad/meilisearch-cdc for linux/amd64 and linux/arm64 on every release tag:
| Tag | Points to |
|---|---|
1.2.3 | That exact release (the Git tag v1.2.3). |
1.2 | The newest release of the 1.2 line. |
latest | The newest final release. Pre-releases such as v1.3.0-rc.1 get their version tag but never latest. |
Pin an exact version in production and upgrade on purpose (see Upgrading).
What is in the image
| Setting | Value |
|---|---|
| Base | gcr.io/distroless/static-debian12:nonroot (no shell, no package manager) |
| Binary | /usr/local/bin/meilisearch-cdc (the entrypoint) |
| User | nonroot:nonroot (uid and gid 65532 in distroless images) |
| Environment | CDC_LISTEN=:8080, CDC_DATA_DIR=/data |
| Volume | /data |
| Port | 8080 |
docker run
docker volume create cdc-data
docker run -d --name cdc --restart unless-stopped \
-p 127.0.0.1:8080:8080 \
-v cdc-data:/data \
-e CDC_PUBLIC_URL=https://cdc.example.com \
ghcr.io/ja7ad/meilisearch-cdc:1.2.3
docker logs -f cdc
Open the dashboard and create the admin account (or pass CDC_ADMIN_EMAIL and CDC_ADMIN_PASSWORD). Flags work too, since the binary is the entrypoint: docker run … ghcr.io/ja7ad/meilisearch-cdc:1.2.3 -log-format json.
Bind mounts and the non-root user
A named volume is initialised with the right owner. With a bind mount (-v /srv/cdc:/data) the host directory must be writable by uid 65532: sudo install -d -o 65532 -g 65532 -m 0700 /srv/cdc. Otherwise the service cannot create cdc.db and exits.
Secrets as files
Every secret setting also reads a file named by <NAME>_FILE, which matches Docker and Kubernetes secrets. Trailing newlines are removed.
printf '%s' "$(openssl rand -base64 32)" > cdc-secret-key
docker run -d --name cdc -p 8080:8080 -v cdc-data:/data \
-v "$PWD/cdc-secret-key:/run/secrets/cdc-key:ro" \
-e CDC_SECRET_KEY_FILE=/run/secrets/cdc-key \
ghcr.io/ja7ad/meilisearch-cdc:1.2.3
Supported: CDC_SECRET_KEY_FILE, CDC_SECRET_KEY_PREVIOUS_FILE, MEILI_API_KEY_FILE, CDC_ADMIN_TOKEN_FILE, CDC_ADMIN_PASSWORD_FILE. When the file cannot be read, the plain variable is used instead.
The admin CLI inside the container
The image has no shell, but the binary runs fine with docker exec. It inherits the container's environment, so it finds /data and the key:
docker exec -it cdc meilisearch-cdc admin users list
docker exec -it cdc meilisearch-cdc admin users reset-password --email you@example.com --generate
Docker Compose
A production-style Compose file with Meilisearch and secrets kept in files:
services:
meilisearch:
image: getmeili/meilisearch:latest # pin the version you run
environment:
MEILI_MASTER_KEY: ${MEILI_MASTER_KEY} # from an .env file next to this one
MEILI_NO_ANALYTICS: "true"
volumes: [meili:/meili_data]
cdc:
image: ghcr.io/ja7ad/meilisearch-cdc:1.2.3
restart: unless-stopped
depends_on: [meilisearch]
ports: ["127.0.0.1:8080:8080"]
environment:
CDC_SECRET_KEY_FILE: /run/secrets/cdc_secret_key
CDC_PUBLIC_URL: https://cdc.example.com
CDC_TRUST_PROXY: "true"
CDC_COOKIE_SECURE: "true"
CDC_LOG_FORMAT: json
GOMEMLIMIT: 400MiB # about 80% of the memory limit
volumes: [cdc:/data]
secrets: [cdc_secret_key]
deploy:
resources:
limits: {memory: 512M}
secrets:
cdc_secret_key: {file: ./secrets/cdc_secret_key}
volumes: {meili: {}, cdc: {}}
Add the Meilisearch destination in the dashboard (URL http://meilisearch:7700), or pin the service to it with MEILI_URL and MEILI_API_KEY_FILE (single-instance mode).
The demo stack
The repository's deploy/docker-compose.yml builds the service from source and starts it next to Meilisearch and PostgreSQL 17 with a seeded products table:
docker compose -f deploy/docker-compose.yml up --build # or: make demo
| Service | Port | Notes |
|---|---|---|
| meilisearch | 7700 | master key masterKey |
| postgres | 5432 | wal_level=logical; user, password and database shop; seeded by deploy/seed.sql |
| cdc | 8080 | admin admin@example.com / change-me-please, MEILI_URL set, CDC_TASKS_FILE=/etc/cdc/tasks.yaml |
The task in deploy/tasks.yaml syncs products with soft deletes on deleted_at, Unix timestamps, a _geo field and managed index settings; it is shown in full on the declarative tasks page. Remove everything with docker compose -f deploy/docker-compose.yml down -v.
Build the image yourself
docker build -t meilisearch-cdc --build-arg VERSION=v1.2.3 .
# or: make docker
The Dockerfile cross-compiles in the build stage (golang:1.27.3-alpine, CGO_ENABLED=0), so multi-architecture builds need no emulation: docker buildx build --platform linux/amd64,linux/arm64 ….