Skip to main content

Deploy identsphere-server with Docker

identsphere-server is the language-agnostic way to run IdentSphere. It's a single, statically-linked Rust binary that reads configuration from environment variables and listens on HTTP.

What ships in the image

  • The compiled identsphere-server binary (~30 MB stripped + LTO'd)
  • The SQL migration files
  • Distroless base image — no shell, no package manager, no SUID
  • Non-root user by default

Total compressed image size: ~50 MB.

Pull or build

Pull (when published)

docker pull ghcr.io/identsphere/server:latest

Build from source

git clone https://github.com/IdentSphere/server
cd IdentSphere
docker build -t identsphere-server:latest .

The Dockerfile uses --mount=type=cache so subsequent builds are fast. First build is ~5 minutes; subsequent builds are ~30 seconds.

Run

The minimal command:

docker run -p 4000:4000 \
-e DATABASE_URL=postgres://user:pass@postgres:5432/auth \
-e IDENTSPHERE_JWT_SECRET="$(openssl rand -base64 48)" \
-e IDENTSPHERE_PUBLIC_BASE_URL=https://auth.example.com \
-e IDENTSPHERE_FROM_EMAIL=no-reply@example.com \
-e IDENTSPHERE_COOKIES_SECURE=true \
-e CORS_ALLOW_ORIGINS=https://app.example.com \
identsphere-server:latest

IdentSphere runs all pending migrations on first boot. Subsequent boots see no pending migrations and start in <1 second.

docker-compose example

# docker-compose.yml
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: IdentSphere
POSTGRES_PASSWORD: ${PG_PASSWORD}
POSTGRES_DB: IdentSphere
volumes:
- identsphere-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U IdentSphere"]
interval: 5s
retries: 10

IdentSphere:
image: ghcr.io/identsphere/server:latest
depends_on:
postgres:
condition: service_healthy
environment:
DATABASE_URL: postgres://IdentSphere:${PG_PASSWORD}@postgres:5432/IdentSphere
IDENTSPHERE_JWT_SECRET: ${JWT_SECRET}
IDENTSPHERE_PUBLIC_BASE_URL: https://auth.example.com
IDENTSPHERE_FROM_EMAIL: no-reply@example.com
IDENTSPHERE_COOKIES_SECURE: "true"
CORS_ALLOW_ORIGINS: https://app.example.com
IDENTSPHERE_LOG_FORMAT: json
RUST_LOG: info,identsphere=debug
ports:
- "4000:4000"
volumes:
- identsphere-uploads:/var/lib/identsphere/uploads
restart: unless-stopped

volumes:
identsphere-data:
identsphere-uploads:

Production hardening

ConcernWhat to do
TLSPut nginx / Caddy / Traefik / Cloudflare in front. Don't terminate TLS in identsphere-server.
SecretsDon't put IDENTSPHERE_JWT_SECRET in your compose file. Use Docker Secrets / Vault / AWS Secrets Manager.
LogsSet IDENTSPHERE_LOG_FORMAT=json and pipe to your log aggregator (Loki, Datadog, etc.).
CORSSet CORS_ALLOW_ORIGINS explicitly. Never use the dev permissive fallback.
Resource limitsSet --memory=512m --cpus=1 — identsphere-server is small.
Health checkwget -q http://localhost:4000/.well-known/jwks.json works as a readiness probe.
Reverse proxy IPSend X-Forwarded-For so IdentSphere gets the real client IP (used in audit logs + IP-allowlist enforcement).

Verify the deploy

# Should return a non-empty JWKS once RSA is configured, or `{"keys":[]}` for HS256-only.
curl https://auth.example.com/.well-known/jwks.json

# Register a user
curl -X POST https://auth.example.com/v1/auth/register \
-H 'Content-Type: application/json' \
-d '{"email":"alice@example.com","password":"correcthorsebattery","organization_name":"Acme"}'

If both work, you're done.

See also