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-serverbinary (~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
| Concern | What to do |
|---|---|
| TLS | Put nginx / Caddy / Traefik / Cloudflare in front. Don't terminate TLS in identsphere-server. |
| Secrets | Don't put IDENTSPHERE_JWT_SECRET in your compose file. Use Docker Secrets / Vault / AWS Secrets Manager. |
| Logs | Set IDENTSPHERE_LOG_FORMAT=json and pipe to your log aggregator (Loki, Datadog, etc.). |
| CORS | Set CORS_ALLOW_ORIGINS explicitly. Never use the dev permissive fallback. |
| Resource limits | Set --memory=512m --cpus=1 — identsphere-server is small. |
| Health check | wget -q http://localhost:4000/.well-known/jwks.json works as a readiness probe. |
| Reverse proxy IP | Send 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
- Production checklist
- Cloudflare Pages (docs hosting)
- Kubernetes
- Cloud platforms — Fly.io, Railway, Render, AWS Fargate, GCP Cloud Run