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

  1. Open http://your-server-ip:8080 in your browser
  2. Log in with username admin and the password from your .env file (or the default admin if you didn’t set OPENPROJECT_SEED__ADMIN__USER__PASSWORD)
  3. Change the admin password immediately if you used the default
  4. Create your first project from the + Project button
  5. Configure modules (Gantt, Boards, Time tracking) per project under Project settings → Modules

Configuration

SettingEnvironment VariableDescription
Host nameOPENPROJECT_HOST__NAMEDomain or IP:port for URL generation. Must match your access URL.
HTTPSOPENPROJECT_HTTPSSet to true when behind an SSL-terminating reverse proxy.
LanguageOPENPROJECT_DEFAULT__LANGUAGEDefault UI language (en, de, fr, es, etc.).
Secret keyOPENPROJECT_SECRET__KEY__BASESession encryption key. Never change after initial setup — invalidates all sessions.
Web workersOPENPROJECT_WEB_WORKERSNumber of application processes (default: 2). Increase for more concurrent users.
Max threadsRAILS_MAX_THREADSThread 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

ResourceMinimumRecommended (200+ users)
RAM4 GB8 GB
CPU4 cores8 cores
Disk20 GB40 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.

Comments