How to Self-Host OpenProject with Docker Compose
What Is OpenProject?
OpenProject is an open-source project management platform with Gantt charts, agile boards, time tracking, cost reporting, and wiki. It replaces Jira, Asana, Monday.com, and Microsoft Project. Self-hosting gives you full control over project data — useful for teams handling sensitive client work or internal roadmaps.
Prerequisites
- A Linux server (Ubuntu 22.04+ recommended)
- Docker and Docker Compose installed (guide)
- 20 GB of free disk space
- 4 GB of RAM (minimum for up to 200 users)
- A domain name (optional, for remote access)
Docker Compose Configuration
Create a project directory and a docker-compose.yml file:
mkdir -p ~/openproject && cd ~/openproject
services:
openproject:
image: openproject/openproject:17.1.1
container_name: openproject
restart: unless-stopped
ports:
- "8080:80"
environment:
OPENPROJECT_SECRET__KEY__BASE: "${SECRET_KEY_BASE}"
OPENPROJECT_HOST__NAME: "${OPENPROJECT_HOST}"
OPENPROJECT_HTTPS: "false"
OPENPROJECT_DEFAULT__LANGUAGE: "en"
OPENPROJECT_SEED__ADMIN__USER__PASSWORD: "${ADMIN_PASSWORD}"
volumes:
- op-pgdata:/var/openproject/pgdata
- op-assets:/var/openproject/assets
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/health_checks/all"]
interval: 30s
timeout: 10s
retries: 5
start_period: 120s
The all-in-one image bundles PostgreSQL and Memcached inside the container, so no separate database service is needed.
Create a .env file alongside docker-compose.yml:
# Secret key for session encryption — generate with: openssl rand -hex 64
SECRET_KEY_BASE=generate-a-long-random-string-here
# Your domain or IP:port — used for URL generation and host header validation
OPENPROJECT_HOST=localhost:8080
# Initial admin password — change after first login
ADMIN_PASSWORD=change-me-to-a-strong-password
Generate secure values:
sed -i "s/generate-a-long-random-string-here/$(openssl rand -hex 64)/" .env
sed -i "s/change-me-to-a-strong-password/$(openssl rand -base64 16)/" .env
Start the stack:
docker compose up -d
The first startup takes 2-3 minutes while the database initializes and migrations run.
Initial Setup
- Open
http://your-server-ip:8080in your browser - Log in with username
adminand the password from your.envfile (or the defaultadminif you didn’t setOPENPROJECT_SEED__ADMIN__USER__PASSWORD) - Change the admin password immediately if you used the default
- Create your first project from the + Project button
- Configure modules (Gantt, Boards, Time tracking) per project under Project settings → Modules
Configuration
| Setting | Environment Variable | Description |
|---|---|---|
| Host name | OPENPROJECT_HOST__NAME | Domain or IP:port for URL generation. Must match your access URL. |
| HTTPS | OPENPROJECT_HTTPS | Set to true when behind an SSL-terminating reverse proxy. |
| Language | OPENPROJECT_DEFAULT__LANGUAGE | Default UI language (en, de, fr, es, etc.). |
| Secret key | OPENPROJECT_SECRET__KEY__BASE | Session encryption key. Never change after initial setup — invalidates all sessions. |
| Web workers | OPENPROJECT_WEB_WORKERS | Number of application processes (default: 2). Increase for more concurrent users. |
| Max threads | RAILS_MAX_THREADS | Thread pool per worker (default: 16). |
Note: OpenProject uses double underscores (__) in environment variable names to represent dots and nested keys. For example, OPENPROJECT_HOST__NAME maps to the host.name setting.
Advanced Configuration (Optional)
Using an External PostgreSQL Database (Slim Image)
For production deployments, use the slim image with an external database:
services:
db:
image: postgres:16-alpine
container_name: openproject-db
restart: unless-stopped
environment:
POSTGRES_USER: openproject
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
POSTGRES_DB: openproject
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U openproject"]
interval: 10s
timeout: 5s
retries: 5
cache:
image: memcached:1.6-alpine
container_name: openproject-cache
restart: unless-stopped
openproject:
image: openproject/openproject:17.1.1-slim
container_name: openproject
restart: unless-stopped
ports:
- "8080:80"
environment:
DATABASE_URL: "postgresql://openproject:${POSTGRES_PASSWORD}@db:5432/openproject"
OPENPROJECT_CACHE__MEMCACHE__SERVER: "cache:11211"
OPENPROJECT_SECRET__KEY__BASE: "${SECRET_KEY_BASE}"
OPENPROJECT_HOST__NAME: "${OPENPROJECT_HOST}"
OPENPROJECT_HTTPS: "false"
volumes:
- op-assets:/var/openproject/assets
depends_on:
db:
condition: service_healthy
volumes:
pgdata:
op-assets:
S3-Compatible Storage for Attachments
environment:
OPENPROJECT_ATTACHMENTS__STORAGE: "fog"
OPENPROJECT_FOG__DIRECTORY: "your-bucket-name"
OPENPROJECT_FOG__CREDENTIALS__PROVIDER: "AWS"
OPENPROJECT_FOG__CREDENTIALS__AWS__ACCESS__KEY__ID: "your-key"
OPENPROJECT_FOG__CREDENTIALS__AWS__SECRET__ACCESS__KEY: "your-secret"
OPENPROJECT_FOG__CREDENTIALS__REGION: "us-east-1"
Reverse Proxy
OpenProject runs on port 80 internally (mapped to 8080 by default). When using a reverse proxy with SSL, set OPENPROJECT_HTTPS=true in your environment.
For detailed setup: Reverse Proxy Setup
Backup
For the all-in-one image, back up both volumes:
# Database backup
docker exec openproject pg_dump -U openproject openproject > openproject-backup-$(date +%Y%m%d).sql
# Assets backup (attachments, uploaded files)
docker run --rm -v openproject_op-assets:/data -v $(pwd):/backup alpine \
tar czf /backup/openproject-assets-$(date +%Y%m%d).tar.gz -C /data .
For the slim image with external PostgreSQL, back up the database service separately. See Backup Strategy.
Troubleshooting
First startup takes several minutes
Symptom: Container shows “starting” for 2-3 minutes before becoming healthy.
Fix: This is normal. Database migrations, asset compilation, and seeding run on first boot. Check progress with docker logs -f openproject.
Permission denied on volume mounts
Symptom: Container fails with Permission denied errors on /var/openproject/.
Fix: OpenProject runs internal processes as specific UIDs. Ensure the host directories are writable. On macOS, use user-owned directories instead of system paths.
Host header mismatch errors
Symptom: Blocked host errors or redirect loops when accessing through a domain.
Fix: Set OPENPROJECT_HOST__NAME to exactly match your access URL (e.g., projects.example.com without the scheme).
Cannot access admin panel after password change
Symptom: Locked out of admin account. Fix: Reset the admin password from the command line:
docker exec -it openproject bash -c "RAILS_ENV=production bundle exec rails runner \"User.find_by(login: 'admin').update(password: 'newpassword', password_confirmation: 'newpassword')\""
Resource Requirements
| Resource | Minimum | Recommended (200+ users) |
|---|---|---|
| RAM | 4 GB | 8 GB |
| CPU | 4 cores | 8 cores |
| Disk | 20 GB | 40 GB+ (grows with attachments) |
OpenProject is the heaviest app in this category. For solo or small-team use, consider Plane or Planka as lighter alternatives.
Verdict
OpenProject is the most feature-complete self-hosted Jira alternative. Gantt charts, agile boards, time tracking, cost reporting, wiki, and meeting management — it covers the full project management lifecycle. The trade-off is resource requirements: 4 GB RAM minimum puts it out of reach for low-powered servers.
For teams that need enterprise-grade project management with Gantt charts and resource planning, OpenProject is the clear choice. If you just need a Kanban board, Planka or Plane are lighter options. For task tracking without the project management overhead, see Vikunja.
Related
Get self-hosting tips in your inbox
New guides, comparisons, and setup tutorials — delivered weekly. No spam.