Last updated 2026-05-28
Docker
The reference deployment shape is the published multi-arch container
image at ghcr.io/elloloop/notify. It is a
FROM scratch binary plus CA certificates — no shell, no
package manager, ~10 MB.
When you'd use this
This is the recommended path for every environment except library mode. Local dev, staging, production: same image, different env vars. If you want to embed notify in another Go binary, see library usage in the README.
Pull and run
docker pull ghcr.io/elloloop/notify:latest
docker run --rm \ -p 8080:8080 -p 8081:8081 -p 9090:9090 \ -e NOTIFY_STORE_DRIVER=memory \ -e NOTIFY_AUTH_JWT_SECRET=$(openssl rand -hex 32) \ -e NOTIFY_INTERNAL_TOKEN=$(openssl rand -hex 32) \ -e NOTIFY_EMAIL_PROVIDER=none \ ghcr.io/elloloop/notify:latestPin to a specific tag in CI and production:
docker pull ghcr.io/elloloop/notify:0.1.0Ports
| Port | Listener | Audience |
|---|---|---|
| 8080 | NotificationClientService | Browser / mobile recipients (Connect HTTP/2) |
| 8081 | NotificationInternalService | Backend producers (gRPC / Connect) |
| 9090 | /healthz and /metrics | Orchestrators / Prometheus |
Image layout
The Dockerfile builds a FROM --platform=$BUILDPLATFORM
golang:1.26.3-alpine3.23 stage that produces a static
binary, then COPYs the binary and CA bundle into
FROM scratch. The final image runs as UID
65532 (non-root):
FROM scratch AS server
COPY --from=builder /bin/notifyd /bin/notifydCOPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
EXPOSE 8080 8081 9090USER 65532:65532ENTRYPOINT ["/bin/notifyd"]Healthcheck
The container has no shell — you can't HEALTHCHECK CMD curl …
against itself. Probe from the orchestrator side instead:
# docker-composehealthcheck: test: ["CMD-SHELL", "wget -q -O- http://localhost:9090/healthz | grep -q ok"] interval: 10s timeout: 3s retries: 5For Kubernetes use HTTP probes — see Kubernetes.
docker-compose example
services: notify: image: ghcr.io/elloloop/notify:0.1.0 ports: - "8080:8080" # client - "8081:8081" # internal - "9090:9090" # metrics environment: NOTIFY_STORE_DRIVER: memory NOTIFY_AUTH_JWT_SECRET: ${NOTIFY_AUTH_JWT_SECRET} NOTIFY_INTERNAL_TOKEN: ${NOTIFY_INTERNAL_TOKEN} NOTIFY_LOG_LEVEL: info # Channel providers NOTIFY_EMAIL_PROVIDER: emailservice NOTIFY_EMAIL_SERVICE_ADDRESS: emailservice:50053 NOTIFY_EMAIL_FROM: noreply@example.com NOTIFY_SMS_PROVIDER: twilio NOTIFY_SMS_ACCOUNT_SID: ${TWILIO_ACCOUNT_SID} NOTIFY_SMS_AUTH_TOKEN: ${TWILIO_AUTH_TOKEN} NOTIFY_SMS_FROM: "+15555550000"Image verification
Every published image is signed via cosign keyless OIDC and scanned by Trivy with a HIGH/CRITICAL gate. Verify the signature on pull:
cosign verify ghcr.io/elloloop/notify:0.1.0 \ --certificate-identity-regexp 'https://github\.com/elloloop/notify/' \ --certificate-oidc-issuer https://token.actions.githubusercontent.comBuilding locally
git clone https://github.com/elloloop/notifycd notifydocker build -t notify:dev .
docker run --rm \ -p 8080:8080 -p 8081:8081 -p 9090:9090 \ -e NOTIFY_AUTH_DEV_MODE=true \ -e NOTIFY_STORE_DRIVER=memory \ notify:devRelated
- Configuration — every NOTIFY_* var
- JWT Keys — generating production secrets
- Store Setup — picking a durable backend
- Deployment: Docker — production tuning
- Deployment: Kubernetes — Deployment + Service + Ingress manifests