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

  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.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

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.