Current Docker Image: passbolt/passbolt:5.9.0-1-non-root | Base: Debian 13 | PHP: 8.2
This guide provides detailed instructions for deploying Passbolt using Docker Compose, including production-hardened configuration, environment variables reference, and operational best practices.
The Docker deployment consists of two main services:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Docker Compose Stack β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β ββββββββββββββββββββ βββββββββββββββββββββββ β
β β Passbolt App ββββββββββΊβ MariaDB 10.11 β β
β β (PHP 8.2) β :3306 β (Database) β β
β β Port: 8080 β β Port: 3306 β β
β ββββββββββ¬ββββββββββ βββββββββββββββββββββββ β
β β β
β β SMTP β
β βΌ β
β ββββββββββββββββββββ β
β β External SMTP β β
β β (Port 587) β β
β ββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Create directory and download compose file
sudo mkdir -p /opt/passbolt && cd /opt/passbolt
curl -LO https://download.passbolt.com/ce/docker/docker-compose-ce.yaml
# Verify integrity
curl -LO https://github.com/passbolt/passbolt_docker/releases/latest/download/docker-compose-ce-SHA512SUM.txt
sha512sum -c docker-compose-ce-SHA512SUM.txt
# Configure and start
docker compose -f docker-compose-ce.yaml up -d
docker compose -f docker-compose-ce.yaml \
exec passbolt su -m -c "/usr/share/php/passbolt/bin/cake \
passbolt register_user \
-u admin@your-domain.com \
-f Admin \
-l User \
-r admin" -s /bin/sh www-data
Below is a production-ready docker-compose.yml with all essential configurations:
services:
passbolt:
image: passbolt/passbolt:ce-4.2.0 # Pin specific version for production
container_name: passbolt-app
restart: unless-stopped
depends_on:
mariadb:
condition: service_healthy
# Port mapping (internal:external)
ports:
- "127.0.0.1:8080:8080" # Bind to localhost only, use reverse proxy
# Environment configuration
environment:
# === Application Settings ===
APP_FULL_BASE_URL: https://passbolt.your-domain.com
PASSBOLT_REGISTRATION_PUBLIC_URL: https://passbolt.your-domain.com
# === Database Configuration ===
DATASOURCES_DEFAULT_HOST: mariadb
DATASOURCES_DEFAULT_PORT: 3306
DATASOURCES_DEFAULT_USERNAME: passbolt
DATASOURCES_DEFAULT_PASSWORD: ${DB_PASSWORD:?Set DB_PASSWORD environment variable}
DATASOURCES_DEFAULT_DATABASE: passbolt
DATASOURCES_DEFAULT_ENCODING: utf8
# === Email Configuration (SMTP) ===
EMAIL_DEFAULT_FROM_NAME: "Passbolt"
EMAIL_DEFAULT_FROM: passbolt@your-domain.com
EMAIL_TRANSPORT_DEFAULT_HOST: smtp.your-domain.com
EMAIL_TRANSPORT_DEFAULT_PORT: 587
EMAIL_TRANSPORT_DEFAULT_USERNAME: ${SMTP_USERNAME:?Set SMTP_USERNAME}
EMAIL_TRANSPORT_DEFAULT_PASSWORD: ${SMTP_PASSWORD:?Set SMTP_PASSWORD}
EMAIL_TRANSPORT_DEFAULT_TLS: true
# === Security Settings ===
PASSBOLT_SECURITY_SALT: ${SECURITY_SALT:?Set SECURITY_SALT}
PASSBOLT_SSL_FORCE: true
# === GPG Configuration ===
PASSBOLT_GPG_SERVER_KEY_EMAIL: passbolt@your-domain.com
PASSBOLT_GPG_SERVER_KEY_NAME: "Passbolt Server"
# === Optional: JWT Authentication ===
PASSBOLT_JWT_TOKENS_ENABLED: true
PASSBOLT_JWT_TOKENS_EXPIRES: "+1 hour"
# Volume mounts
volumes:
# GPG key persistence
- passbolt_gpg:/etc/passbolt/gpg
# SSL certificates (if using manual HTTPS)
- ./certs:/etc/ssl/certs/passbolt:ro
# Custom configuration
- ./config/passbolt.php:/etc/passbolt/passbolt.php:ro
# Health check
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health.json"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
# Resource limits
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
cpus: '0.5'
memory: 512M
# Logging configuration
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
mariadb:
image: mariadb:10.11
container_name: passbolt-db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:?Set DB_ROOT_PASSWORD}
MYSQL_DATABASE: passbolt
MYSQL_USER: passbolt
MYSQL_PASSWORD: ${DB_PASSWORD:?Set DB_PASSWORD}
MYSQL_CHARSET: utf8mb4
MYSQL_COLLATION: utf8mb4_unicode_ci
volumes:
- passbolt_db:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
# Named volumes
volumes:
passbolt_gpg:
driver: local
passbolt_db:
driver: local
# Network configuration
networks:
default:
driver: bridge
| Variable | Description | Default | Required |
|---|---|---|---|
APP_FULL_BASE_URL |
Public URL of your Passbolt instance | https://passbolt.local |
β Yes |
PASSBOLT_REGISTRATION_PUBLIC_URL |
URL for user registration | Same as APP_FULL_BASE_URL | β Yes |
PASSBOLT_SECURITY_SALT |
Cryptographic salt for sessions | Auto-generated | β Yes |
| Variable | Description | Default | Required |
|---|---|---|---|
DATASOURCES_DEFAULT_HOST |
Database hostname | mariadb |
β Yes |
DATASOURCES_DEFAULT_PORT |
Database port | 3306 |
β Yes |
DATASOURCES_DEFAULT_USERNAME |
Database username | passbolt |
β Yes |
DATASOURCES_DEFAULT_PASSWORD |
Database password | - | β Yes |
DATASOURCES_DEFAULT_DATABASE |
Database name | passbolt |
β Yes |
| Variable | Description | Default | Required |
|---|---|---|---|
EMAIL_DEFAULT_FROM_NAME |
Sender name | Passbolt |
β Yes |
EMAIL_DEFAULT_FROM |
Sender email | passbolt@localhost |
β Yes |
EMAIL_TRANSPORT_DEFAULT_HOST |
SMTP server hostname | localhost |
β Yes |
EMAIL_TRANSPORT_DEFAULT_PORT |
SMTP port | 25 |
β Yes |
EMAIL_TRANSPORT_DEFAULT_USERNAME |
SMTP username | null |
β οΈ If auth required |
EMAIL_TRANSPORT_DEFAULT_PASSWORD |
SMTP password | null |
β οΈ If auth required |
EMAIL_TRANSPORT_DEFAULT_TLS |
Enable TLS | null |
Recommended |
| Variable | Description | Default | Required |
|---|---|---|---|
PASSBOLT_SSL_FORCE |
Force HTTPS | true |
Recommended |
PASSBOLT_JWT_TOKENS_ENABLED |
Enable JWT API auth | false |
Optional |
PASSBOLT_JWT_TOKENS_EXPIRES |
JWT token expiration | +1 hour |
Optional |
| Variable | Description | Default | Required |
|---|---|---|---|
PASSBOLT_GPG_SERVER_KEY_EMAIL |
Server GPG key email | - | β Yes |
PASSBOLT_GPG_SERVER_KEY_NAME |
Server GPG key name | - | β Yes |
For enhanced security, use Docker secrets instead of environment variables:
services:
passbolt:
image: passbolt/passbolt:ce-4.2.0
secrets:
- db_password
- smtp_password
- security_salt
environment:
DATASOURCES_DEFAULT_PASSWORD_FILE: /run/secrets/db_password
EMAIL_TRANSPORT_DEFAULT_PASSWORD_FILE: /run/secrets/smtp_password
PASSBOLT_SECURITY_SALT_FILE: /run/secrets/security_salt
secrets:
db_password:
external: true
smtp_password:
external: true
security_salt:
external: true
Create secrets:
# Generate and store secrets
openssl rand -base64 32 | docker secret create db_password -
openssl rand -base64 32 | docker secret create security_salt -
echo "your-smtp-password" | docker secret create smtp_password -
Use Nginx, Traefik, or Caddy as reverse proxy with Letβs Encrypt:
Nginx Example:
server {
listen 443 ssl http2;
server_name passbolt.your-domain.com;
ssl_certificate /etc/letsencrypt/live/passbolt.your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/passbolt.your-domain.com/privkey.pem;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
location / {
proxy_pass http://127.0.0.1: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;
}
}
# Create certs directory
mkdir -p /opt/passbolt/certs
# Copy certificates
cp /path/to/fullchain.pem /opt/passbolt/certs/
cp /path/to/privkey.pem /opt/passbolt/certs/
# Add volume mount to docker-compose.yml
volumes:
- ./certs:/etc/ssl/certs/passbolt:ro
# Check container health
docker compose ps
# View application health endpoint
curl http://localhost:8080/health.json
# Check detailed health
docker compose exec passbolt su -m -c \
"/usr/share/php/passbolt/bin/cake passbolt healthcheck" -s /bin/sh www-data
# View live logs
docker compose logs -f passbolt
# Last 100 lines
docker compose logs --tail=100 passbolt
# Database logs
docker compose logs -f mariadb
# Database backup
docker compose exec mariadb mysqldump \
-u root -p${DB_ROOT_PASSWORD} passbolt > backup_$(date +%F).sql
# GPG keys backup
docker compose exec passbolt gpg --export-secret-keys \
--armor passbolt@your-domain.com > gpg_backup_$(date +%F).asc
# Full backup script
#!/bin/bash
BACKUP_DIR="/backup/passbolt/$(date +%F)"
mkdir -p $BACKUP_DIR
docker compose exec mariadb mysqldump \
-u root -p${DB_ROOT_PASSWORD} passbolt > $BACKUP_DIR/database.sql
docker compose exec passbolt gpg --export-secret-keys \
--armor passbolt@your-domain.com > $BACKUP_DIR/gpg-keys.asc
docker compose exec passbolt cat /etc/passbolt/passbolt.php \
> $BACKUP_DIR/passbolt.php
# Compress backup
tar -czf $BACKUP_DIR.tar.gz -C $BACKUP_DIR .
rm -rf $BACKUP_DIR
# Stop application
docker compose down
# Restore database
docker compose up -d mariadb
sleep 10
docker compose exec -T mariadb mysql \
-u root -p${DB_ROOT_PASSWORD} passbolt < backup_2025-01-15.sql
# Restore GPG keys
docker compose exec -T passbolt gpg --import < gpg_backup_2025-01-15.asc
# Start application
docker compose up -d
# 1. Backup first!
./backup-passbolt.sh
# 2. Pull new image
docker compose pull
# 3. Stop containers
docker compose down
# 4. Start with new version
docker compose up -d
# 5. Run migrations
docker compose exec passbolt su -m -c \
"/usr/share/php/passbolt/bin/cake passbolt migrate" -s /bin/sh www-data
# 6. Verify health
docker compose exec passbolt su -m -c \
"/usr/share/php/passbolt/bin/cake passbolt healthcheck" -s /bin/sh www-data
# Check logs
docker compose logs passbolt
# Common issues:
# 1. Database not ready - wait for mariadb health check
# 2. Invalid environment variables - check .env file
# 3. Port conflict - change port mapping
# Regenerate server GPG key
docker compose exec passbolt su -m -c \
"/usr/share/php/passbolt/bin/cake passbolt recover_keypair" -s /bin/sh www-data
# Check NTP sync (critical for GPG)
docker compose exec passbolt date
# Test SMTP connection
docker compose exec passbolt telnet smtp.your-domain.com 587
# Run email health check
docker compose exec passbolt su -m -c \
"/usr/share/php/passbolt/bin/cake passbolt healthcheck --domain email" \
-s /bin/sh www-data
For HA deployments, use external database and shared storage:
services:
passbolt:
image: passbolt/passbolt:ce-4.2.0
deploy:
replicas: 2
resources:
limits:
cpus: '2.0'
memory: 2G
volumes:
# Shared GPG storage (NFS, Ceph, etc.)
- type: volume
source: passbolt_gpg_shared
target: /etc/passbolt/gpg
volume:
nocopy: true
# External database
environment:
DATASOURCES_DEFAULT_HOST: mariadb-cluster.internal
# Add Prometheus metrics exporter
services:
passbolt:
ports:
- "9090:9090" # Metrics endpoint
environment:
PASSBOLT_METRICS_ENABLED: true
Any questions?
Feel free to contact us. Find all contact information on our contact page.