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.2.0
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.2.0-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.
Frequently Asked Questions
How does OpenProject compare to Jira?
OpenProject covers most of Jira’s core features — agile boards, backlog management, sprints, work packages (issues), and time tracking. It adds Gantt charts and cost reporting that Jira charges extra for. Jira has a larger plugin ecosystem and more mature workflow customization. OpenProject uses more RAM (4 GB minimum) but has no per-user licensing fees. For teams already on Jira, the migration involves recreating project structures — there’s no automated import.
Can OpenProject run on a Raspberry Pi?
No. OpenProject requires 4 GB of RAM minimum (Ruby on Rails + PostgreSQL + Memcached). Even a Raspberry Pi 5 with 8 GB would struggle under moderate load. For lightweight project management on low-powered hardware, consider Planka (~200 MB RAM) or Vikunja (~50 MB RAM).
Does OpenProject support agile (Scrum/Kanban)?
Yes. OpenProject includes both Scrum boards (with sprints, story points, burndown charts) and Kanban boards. Each project can enable either or both via Project Settings → Modules. The agile boards are drag-and-drop with configurable columns and swimlanes. It also supports backlog management with velocity tracking.
What’s the difference between the all-in-one and slim images?
The all-in-one image (openproject/openproject:17.2.0) bundles PostgreSQL and Memcached inside the container — zero external dependencies. The slim image (openproject/openproject:17.2.0-slim) requires an external PostgreSQL and Memcached service. Use all-in-one for simplicity; use slim for production deployments where you want to manage the database separately for better backup control and scaling.
Does OpenProject support LDAP or SSO?
Yes. OpenProject supports LDAP authentication, SAML 2.0, and OpenID Connect out of the box. Configure these in Administration → Authentication. This allows single sign-on with providers like Keycloak, Authentik, Azure AD, or corporate Active Directory. The Community Edition includes LDAP; SAML and OIDC are available in all editions.
How do I back up OpenProject?
For the all-in-one image, use docker exec openproject pg_dump -U openproject openproject > backup.sql to dump the database, and back up the op-assets volume for uploaded files. For the slim image, back up your external PostgreSQL database separately. Always test restores periodically — a backup you’ve never restored is not a backup.
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments