Docker Compose
Deploy Hanzo KMS using Docker Compose for development, testing, or small-scale production environments.
Learn how to deploy Hanzo KMS using Docker Compose. This method runs Hanzo KMS and its dependencies (PostgreSQL and Redis) as containers on a single Docker host. It's ideal for trying out Hanzo KMS, development environments, or small-scale deployments that don't require high availability.
Prerequisites
- A Linux server (Ubuntu 20.04+ recommended) or macOS/Windows with Docker Desktop
- Docker Engine (version 20.10+)
- Docker Compose (version 2.0+ recommended)
This Docker Compose configuration is not designed for high-availability production scenarios. It includes just the essential components needed to set up an Hanzo KMS proof of concept (POC). To run Hanzo KMS in a highly available manner, see the Docker Swarm guide or Kubernetes guide.
System Requirements
The following are minimum requirements for running Hanzo KMS with Docker Compose:
| Component | Minimum | Recommended |
|---|---|---|
| CPU | 2 cores | 4 cores |
| RAM | 4 GB | 8 GB |
| Disk | 20 GB | 50 GB+ (SSD recommended) |
These requirements include resources for Hanzo KMS, PostgreSQL, and Redis containers. For larger deployments with many secrets or users, increase resources accordingly.
Deployment Steps
Confirm that Docker and Docker Compose are installed on your machine:
docker --version
docker compose versionYou should see version information for both commands. If not, install Docker and Docker Compose following the official documentation.
You can obtain the Hanzo KMS docker compose file by using a command-line downloader such as wget or curl. If your system doesn't have either of these, you can use an equivalent command that works with your machine.
curl -o docker-compose.prod.yml https://raw.githubusercontent.com/Hanzo KMS/kms/main/docker-compose.prod.ymlwget -O docker-compose.prod.yml https://raw.githubusercontent.com/Hanzo KMS/kms/main/docker-compose.prod.ymlHanzo KMS requires a set of credentials used for connecting to dependent services such as Postgres, Redis, etc. The default credentials can be downloaded using one of the commands listed below.
curl -o .env https://raw.githubusercontent.com/Hanzo KMS/kms/main/.env.examplewget -O .env https://raw.githubusercontent.com/Hanzo KMS/kms/main/.env.exampleOnce downloaded, the credentials file will be saved to your working directory as .env file. View all available configurations here.
The .env file contains secrets that control the security of your deployment. Do not commit it to version control, and protect access to it:
chmod 600 .envStart all services in detached mode:
docker compose -f docker-compose.prod.yml up -dThis command starts three containers:
- backend: The main Hanzo KMS application (exposed on host port 80, internal port 8080)
- db: PostgreSQL database for storing encrypted secrets
- redis: Redis for caching and job queues
Check that all containers are running:
docker compose -f docker-compose.prod.yml psYou should see all three services with a status of Up. Next, verify Hanzo KMS is responding:
curl -s http://localhost:80/api/status | head -c 100If successful, open your browser and navigate to http://localhost:80 (or your configured domain) to create your admin account.
The first user to sign up becomes the instance administrator. Make sure to complete this step before exposing Hanzo KMS to others.
Podman Compose is an alternative way to run Hanzo KMS using Podman as a replacement for Docker. Podman is backwards compatible with Docker Compose files.
Confirm that Podman and Podman Compose are installed:
podman version
podman-compose versionIf not installed, follow the Podman installation guide and Podman Compose guide.
You can obtain the Hanzo KMS docker compose file by using a command-line downloader such as wget or curl. If your system doesn't have either of these, you can use an equivalent command that works with your machine.
curl -o docker-compose.prod.yml https://raw.githubusercontent.com/Hanzo KMS/kms/main/docker-compose.prod.ymlwget -O docker-compose.prod.yml https://raw.githubusercontent.com/Hanzo KMS/kms/main/docker-compose.prod.ymlHanzo KMS requires a set of credentials used for connecting to dependent services such as Postgres, Redis, etc. The default credentials can be downloaded using one of the commands listed below.
curl -o .env https://raw.githubusercontent.com/Hanzo KMS/kms/main/.env.examplewget -O .env https://raw.githubusercontent.com/Hanzo KMS/kms/main/.env.exampleOnce downloaded, the credentials file will be saved to your working directory as .env file. View all available configurations here.
Initialize and start the Podman machine:
podman machine init --now
podman machine set --rootful
podman machine startIf using a rootless Podman install, you can skip podman machine set --rootful.
podman-compose -f docker-compose.prod.yml up -dAccess the UI at http://localhost:80.
Managing Your Deployment
Stopping and Restarting Services
To stop all Hanzo KMS services:
docker compose -f docker-compose.prod.yml downTo restart services:
docker compose -f docker-compose.prod.yml up -dTo restart a specific service (e.g., after configuration changes):
docker compose -f docker-compose.prod.yml restart backendData Persistence and Volumes
The Docker Compose configuration uses named volumes to persist data:
| Volume | Purpose | Data Stored |
|---|---|---|
pg_data | PostgreSQL data | All encrypted secrets, users, projects, and configuration |
redis_data | Redis data | Cache and job queue data (can be regenerated) |
Critical: The pg_data volume contains all your encrypted secrets. Never delete this volume unless you intend to lose all data. Always back up before any maintenance operations.
To view volumes:
docker volume ls | grep -E "pg_data|redis_data"To back up the PostgreSQL volume:
docker compose -f docker-compose.prod.yml exec db pg_dump -U kms kms > backup_$(date +%Y%m%d_%H%M%S).sqlViewing Logs
To view logs from all services:
docker compose -f docker-compose.prod.yml logs -fTo view logs from a specific service:
docker compose -f docker-compose.prod.yml logs -f backend
docker compose -f docker-compose.prod.yml logs -f db
docker compose -f docker-compose.prod.yml logs -f redisTo view the last 100 lines of logs:
docker compose -f docker-compose.prod.yml logs --tail=100 backendLogs are also available directly from Docker:
docker logs <container_name> --since 1hAdditional Configuration
Configure your firewall to allow traffic to Hanzo KMS while securing other services:
Required ports:
| Port | Service | Access |
|---|---|---|
| 80 | HTTP (Hanzo KMS) | Public (or internal network) |
| 443 | HTTPS (if using TLS) | Public (or internal network) |
Internal ports (should NOT be exposed publicly):
| Port | Service | Notes |
|---|---|---|
| 5432 | PostgreSQL | Container-to-container only |
| 6379 | Redis | Container-to-container only |
UFW (Ubuntu/Debian):
# Allow HTTP/HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Ensure database ports are not exposed
sudo ufw deny 5432/tcp
sudo ufw deny 6379/tcp
sudo ufw enablefirewalld (RHEL/CentOS):
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reloadNever expose PostgreSQL (5432) or Redis (6379) ports to the public internet. These should only be accessible within the Docker network.
Hanzo KMS uses email for user invitations, password resets, and notifications. Configure SMTP by adding these variables to your .env file:
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USERNAME=your-smtp-username
SMTP_PASSWORD=your-smtp-password
SMTP_FROM_ADDRESS=kms@example.com
SMTP_FROM_NAME=Hanzo KMSCommon SMTP providers:
SMTP_HOST=email-smtp.us-east-1.amazonaws.com
SMTP_PORT=587
SMTP_USERNAME=your-ses-smtp-username
SMTP_PASSWORD=your-ses-smtp-password
SMTP_FROM_ADDRESS=noreply@yourdomain.com
SMTP_FROM_NAME=Hanzo KMSSMTP_HOST=smtp.sendgrid.net
SMTP_PORT=587
SMTP_USERNAME=apikey
SMTP_PASSWORD=your-sendgrid-api-key
SMTP_FROM_ADDRESS=noreply@yourdomain.com
SMTP_FROM_NAME=Hanzo KMSSMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USERNAME=your-email@gmail.com
SMTP_PASSWORD=your-app-password
SMTP_FROM_ADDRESS=your-email@gmail.com
SMTP_FROM_NAME=Hanzo KMSFor Gmail, you must use an App Password instead of your regular password.
After updating the .env file, restart Hanzo KMS:
docker compose -f docker-compose.prod.yml restart backendWe recommend securing your Hanzo KMS deployment with HTTPS using a reverse proxy like NGINX.
1. Obtain SSL certificates (e.g., via Let's Encrypt):
sudo apt install certbot
sudo certbot certonly --standalone -d secrets.example.com2. Create NGINX configuration directory:
mkdir -p ./nginx/certs
sudo cp /etc/letsencrypt/live/secrets.example.com/fullchain.pem ./nginx/certs/
sudo cp /etc/letsencrypt/live/secrets.example.com/privkey.pem ./nginx/certs/3. Add NGINX service to docker-compose.prod.yml:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/certs:/etc/nginx/certs:ro
depends_on:
- backend4. Create ./nginx/nginx.conf:
events {}
http {
server {
listen 80;
server_name secrets.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name secrets.example.com;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
location / {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}5. Update .env:
SITE_URL=https://secrets.example.com6. Restart services:
docker compose -f docker-compose.prod.yml down
docker compose -f docker-compose.prod.yml up -dFor higher availability, use external managed services like AWS RDS and ElastiCache.
Update your .env:
DB_CONNECTION_URI=postgresql://<user>:<password>@<host>:5432/<dbname>
REDIS_URL=redis://:<password>@<host>:6379Remove or comment out db and redis services in docker-compose.prod.yml.
Ensure firewall/VPC access to these services is properly configured.
Regular backups are critical for protecting your data.
Database backup:
docker compose -f docker-compose.prod.yml exec db pg_dump -U kms kms > backup_$(date +%Y%m%d).sqlAutomated daily backups (cron):
# Add to crontab (crontab -e)
0 2 * * * cd /path/to/kms && docker compose -f docker-compose.prod.yml exec -T db pg_dump -U kms kms > /backups/kms_$(date +\%Y\%m\%d).sqlRestore from backup:
docker compose -f docker-compose.prod.yml exec -T db psql -U kms kms < backup.sqlAlso back up your ENCRYPTION_KEY from the .env file. Without this key, you cannot decrypt secrets even if you restore the database.
Hanzo KMS exposes Prometheus metrics when enabled.
1. Add to .env:
OTEL_TELEMETRY_COLLECTION_ENABLED=true
OTEL_EXPORT_TYPE=prometheus2. Expose port 9464 in docker-compose.prod.yml by adding to the backend service:
ports:
- "80:8080"
- "9464:9464"3. Configure Prometheus to scrape localhost:9464 or the container DNS.
See the Monitoring Guide for full setup.
Keeping Hanzo KMS up-to-date ensures you receive the latest features and security patches.
1. Back up your database:
docker compose -f docker-compose.prod.yml exec db pg_dump -U kms kms > backup_before_upgrade.sql2. Update the image tag in docker-compose.prod.yml to the desired version.
3. Pull and restart:
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d4. Verify the upgrade:
docker compose -f docker-compose.prod.yml logs -f backendWatch for successful database migration messages. See the Upgrade Guide for more details.
Troubleshooting
Check container status:
docker compose -f docker-compose.prod.yml ps -aView startup logs:
docker compose -f docker-compose.prod.yml logsCommon causes:
- Port 80 already in use: Stop other services or change the port mapping
- Insufficient memory: Ensure at least 4GB RAM is available
- Invalid
.envfile: Check for syntax errors or missing required variables
Check if PostgreSQL is running:
docker compose -f docker-compose.prod.yml exec db pg_isreadyVerify database credentials:
docker compose -f docker-compose.prod.yml exec db psql -U kms -c "SELECT 1"Check Hanzo KMS logs for connection errors:
docker compose -f docker-compose.prod.yml logs backend | grep -i "database\|postgres\|connection"If the database was corrupted, you may need to restore from backup or recreate the volume:
docker compose -f docker-compose.prod.yml down -v # WARNING: Deletes all data
docker compose -f docker-compose.prod.yml up -dVerify the service is running:
curl -v http://localhost:80/api/statusCheck if the port is listening:
sudo netstat -tlnp | grep :80
# or
sudo ss -tlnp | grep :80Check firewall rules:
sudo ufw status
# or
sudo firewall-cmd --list-allVerify SITE_URL is correct in your .env file and matches how you're accessing Hanzo KMS.
Test SMTP connectivity:
docker compose -f docker-compose.prod.yml exec backend nc -zv smtp.example.com 587Check Hanzo KMS logs for email errors:
docker compose -f docker-compose.prod.yml logs backend | grep -i "smtp\|email\|mail"Common issues:
- Incorrect SMTP credentials
- Firewall blocking outbound port 587 or 465
- SMTP provider requires specific authentication (e.g., Gmail App Passwords)
Check resource usage:
docker statsCheck for slow database queries:
docker compose -f docker-compose.prod.yml exec db psql -U kms -c "SELECT * FROM pg_stat_activity WHERE state = 'active'"Solutions:
- Increase container memory limits in
docker-compose.prod.yml - Add more CPU cores to the host
- Consider migrating to a managed database for better performance
If you have lost access to your admin account, you can reset it via the database:
# Connect to the database
docker compose -f docker-compose.prod.yml exec db psql -U kms
# Find the user
SELECT id, email FROM users WHERE email = 'admin@example.com';
# The password must be reset through the application password reset flow
# or by deleting and recreating the userFor security reasons, passwords are hashed and cannot be directly reset in the database. Use the "Forgot Password" flow if SMTP is configured, or contact Hanzo KMS support for assistance.
Your Hanzo KMS instance should now be running on port 80 (or 443 if using TLS). Visit http://localhost:80 or https://<your-domain> to access your instance.

How is this guide?
Last updated on