Use case · Project management

OpenProject self hosted on a dedicated server

A self-hosted OpenProject needs, by its own documentation, a quad-core CPU at 2 GHz or more, 4 GB of RAM and 20 GB of free disk for up to 200 users, plus PostgreSQL 16 or newer; the recommended install is the official Docker Compose stack. This page sizes the machine, compares the DEB/RPM packages with Compose, and walks the Compose route through secrets, the first admin login, nginx with HTTPS, backups and upgrades.

See the machines

Hardware and database requirements

The system requirements page sets one floor for a single server with up to 200 total users: a quad-core CPU at 2 GHz or faster, 4096 MB RAM, 20 GB free disk space. The same figures apply to the packaged install, the all-in-one container and Compose. What really drives load is concurrent users, not the total: 1,000 accounts with 20 people working at once need about what 100 accounts with 20 active need.

Total active usersCPU coresRAMWeb workersBackground workersDisk
up to 20044 GB2120 GB
50088 GB4240 GB
1,5001616 GB8480 GB

Those rows are the documentation's own rough guideline; attachments stored on the box come on top of the disk column. Default settings run 4 web workers and 1 background worker, and the docs recommend at least 2 web workers even for 5 users, because one page can fire several requests at once. For the database, OpenProject officially supports PostgreSQL 16 or above since 16.0.0; versions 13 to 15 may still run but are unsupported. It also needs the pg_trgm, btree_gist and unaccent extensions.

DEB/RPM packages or Docker Compose

Both routes install the Ruby runtime and the Puma application server. They differ in who owns the rest of the stack.

  • Packages: apt-get install openproject, then the openproject configure wizard installs PostgreSQL, memcached and an Apache front end with optional TLS. Current package targets are Ubuntu 22.04, Debian 12, RHEL/CentOS 9 and SLES 15; the requirements table marks Ubuntu 20.04 and Debian 11 as past their package end of life. OpenProject does not plan packages for newer releases such as Ubuntu 24.04, and they advise a server with nothing else on it, since the wizard takes over Apache.
  • Docker Compose: the documentation's recommended method. Web, worker, cron, seeder, PostgreSQL, memcached, a Caddy proxy, the hocuspocus collaboration server and an autoheal watchdog: nine services, one container each. Any Linux that runs Docker Engine works, and some future features will ship only for Docker installs.
  • All-in-one container: one docker run for a quick test. The docs advise against it in production because the bundled database is hard to upgrade.

One thing only the packages do: create Git and Subversion repositories from inside OpenProject. The Docker setup can reference external repositories but not host them. If you need that, take the packages on Ubuntu 22.04; otherwise take Compose.

Community and Enterprise editions, and the licence

The installation guide defines the on-premises editions in a sentence each. Community edition: "The free, no license, edition of OpenProject that you install on-premise. The additional add-ons of the Enterprise edition are not included." Enterprise on-premises: "Builds on top of the Community edition: Enterprise add-ons, professional support, hosted on-premises with optional installation support."

The code is under the GNU General Public License v3 (GPLv3); the FAQ notes you may modify it, copy it and redistribute it under those terms. An Enterprise token added to a Community install switches the add-ons on and keeps your data, and if the subscription ends the instance falls back to Community with the data intact. Everything on this page applies to both.

Choosing the machine

Read the floor against the dedicated server lines by cores, clock and memory, in that order. Clock is the trap: these lines on the page run under the 2 GHz minimum, however many cores they carry: the AMD Opteron X2150 and Xeon E3-1284L v3 cartridges (1.8 GHz), the Atom C2730 and Xeon D-1587 (1.7 GHz), the Opteron 6366 HE (1.8 GHz), and the dual-socket E5-2630L v3 (1.8 GHz) and E5-2650L v2 (1.7 GHz). Memory is the other: 4 GB is the documented minimum, and an 8 GB machine leaves PostgreSQL, the stack's nine services and the page cache little headroom once a team uploads files.

  • Up to 200 users: a 4-core cartridge at 2.9 or 3 GHz (Xeon E3-1284L v4, E3-1585L v5) or the 8-core Atom C2750 at 2.4 GHz, with 16 GB rather than 8.
  • Around 500 users: the Xeon E-2286M, 8 cores at 2.4 GHz with 32 GB or more and two NVMe drives, matches the 8-core row with room for PostgreSQL.
  • 1,500 users: a dual-socket Xeon Gold 6130, 32 cores at 2.1 GHz, memory from 16 GB up and SSDs for the database. Racked single-, dual- and quad-socket servers each carry an out-of-band console.

Every line is a whole machine with root, an unmetered port and month-to-month terms; a racked one is handed over in about 30 minutes once payment is approved. Pick Ubuntu or Debian in the configurator: the widest lines run Ubuntu from 14.04 to 26.04.

Installing with Docker Compose

Install Docker Engine 28 or newer with the Compose plugin from Docker's own repository first, then get a root shell (sudo -i). The block below fences the box to SSH (IPv6 included, set before any allow rule), clones the stable/17 branch the docs use, writes both secrets into a mode-600 .env, and starts the stack. The secrets are generated inside printf, a shell builtin, so they appear in neither shell history nor the process list.

sed -i 's/^IPV6=.*/IPV6=yes/' /etc/default/ufw
ufw allow OpenSSH
ufw --force enable
git clone https://github.com/opf/openproject-docker-compose.git --depth=1 --branch=stable/17 /opt/openproject
cd /opt/openproject
cp .env.example .env
chmod 600 .env
sed -i '/^SECRET_KEY_BASE=/d;/^COLLABORATIVE_SERVER_SECRET=/d' .env
printf 'SECRET_KEY_BASE=%s\n' "$(openssl rand -hex 64)" >> .env
printf 'COLLABORATIVE_SERVER_SECRET=%s\n' "$(openssl rand -hex 32)" >> .env
grep -E '^(PORT|OPENPROJECT_HTTPS|OPENPROJECT_HOST__NAME|POSTGRES_VERSION)=' .env
mkdir -p /var/openproject/assets
chown 1000:1000 -R /var/openproject/assets
docker compose up -d --build --pull always

Check the grep output before the last line runs. It should read PORT=127.0.0.1:8080, OPENPROJECT_HTTPS=false, OPENPROJECT_HOST__NAME=localhost and a POSTGRES_VERSION=17 line with a comment after it. PORT is not an OpenProject setting: it only sets the address and port the proxy container is published on. The docs warn that without it the port is bound to 0.0.0.0, which is public. A mapping with no address is published on both 0.0.0.0 and [::]; Docker's rules skip ufw on IPv4, and on IPv6 too once Docker's own IPv6 networking is enabled. Publishing on 127.0.0.1 only is local-only on Docker Engine 28+. If the POSTGRES_VERSION line is missing, do not start: the compose file then falls back to PostgreSQL 13, which OpenProject no longer supports. The sample database password never leaves the internal backend network: the db service publishes no port.

SECRET_KEY_BASE derives the application's keys. Per the docs it is a password-grade value that must stay constant: changing it invalidates every session and 2FA remember token, and losing it leaves encrypted database content unreadable. OpenProject refuses to start with a default or weak value. COLLABORATIVE_SERVER_SECRET replaces the sample secret the docs explicitly tell you to override.

The first login, before anything is public

Every new install seeds one administrator with login admin and password admin, and OpenProject forces a password change on that account's first login. Do that while the only way in is your own SSH session.

  1. From your workstation: ssh -L 8080:127.0.0.1:8080 you@your-server, with your own login, not a service account.
  2. Open http://localhost:8080. The seeder needs a few minutes on first start; docker compose logs -f web shows when it is done.
  3. Sign in as admin/admin, set a long password, then add your own email to the admin account.
  4. Only now point DNS at the machine and open the web ports.

Hold the team invitations until the real host name, HTTPS and outgoing mail are configured. Links in invitation emails are built from the host name, and while it is still localhost they lead nowhere.

Host name, nginx and HTTPS

OpenProject reads its configuration from environment variables: an OPENPROJECT_ prefix, setting names in upper case, nested keys joined by underscores and underscores inside a key doubled, hence OPENPROJECT_HOST__NAME. In Compose, the header of .env.example notes that its values only apply when docker-compose.yml passes them to the containers, which it does for the host name, HTTPS, the secrets and the database URL; anything else, SMTP's EMAIL_DELIVERY_METHOD, SMTP_ADDRESS and the rest included, goes in the environment section of a docker-compose.override.yml, because docker-compose.yml is overwritten on every pull. On packages: sudo openproject config:set VARIABLE=value, then sudo openproject configure.

Going public means three variables and a front end. The host name must match what browsers request, HTTPS mode goes back on (the docs strongly advise against running production with it off, since it drops secure cookies), and the collaboration URL moves to wss://. nginx then terminates TLS and sends X-Forwarded-Proto https, which tells OpenProject the request arrived encrypted. Run it as root once DNS points at the server, with pm.example.com replaced by your name throughout.

cd /opt/openproject
sed -i -e 's/^OPENPROJECT_HOST__NAME=.*/OPENPROJECT_HOST__NAME=pm.example.com/' \
  -e 's/^OPENPROJECT_HTTPS=.*/OPENPROJECT_HTTPS=true/' \
  -e 's|^COLLABORATIVE_SERVER_URL=.*|COLLABORATIVE_SERVER_URL=wss://pm.example.com/hocuspocus|' .env
docker compose up -d
apt install -y nginx certbot python3-certbot-nginx
cat > /etc/nginx/sites-available/openproject <<'EOF'
server {
  listen 80;
  listen [::]:80;
  server_name pm.example.com;
  client_max_body_size 256m;
  location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }
}
EOF
ln -s /etc/nginx/sites-available/openproject /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
ufw allow 80,443/tcp
certbot --nginx -d pm.example.com

Two lines are ours rather than the docs': the WebSocket upgrade headers, needed for the /hocuspocus path the bundled Caddy forwards to the collaboration server, and client_max_body_size, because nginx refuses uploads above 1 MB by default. nginx runs on the host, not in Docker, so ufw does filter 80 and 443 and the allow rule is required for both IPv4 and IPv6 visitors and for the Let's Encrypt check.

Backups and upgrades

The backing-up guide lists what to keep: the database, configuration, attachments and any repositories. On Compose the repository ships a backup control that tars the PostgreSQL data directory and the assets into ./backups, with the stack stopped: docker compose down, docker compose -f docker-compose.yml -f docker-compose.control.yml build, the same pair of files with run backup, then docker compose up -d. For a nightly copy without downtime, a pg_dump (the docs' own flags) plus a tar of the assets works from cron. Create the target first with install -d -m 700 /var/backups/openproject, then save this as a file:

# File: /etc/cron.d/openproject-backup
30 2 * * * root umask 077; cd /opt/openproject && docker compose exec -T db pg_dump -U postgres -d openproject -x -O > /var/backups/openproject/db-$(date +\%F).sql && tar czf /var/backups/openproject/assets-$(date +\%F).tar.gz -C /var/openproject/assets . && cp .env /var/backups/openproject/env-$(date +\%F)

The .env copy matters: a restored database without its SECRET_KEY_BASE loses its encrypted content. On packages, sudo openproject run backup writes database, attachments, configuration and repositories to /var/db/openproject/backup. Either way, copy the files off this machine yourself; a backup on the disk it protects is not one.

Upgrades: back up first. Within 17.x on Compose, git pull the repository, then docker compose pull --ignore-buildable and docker compose up -d. The .env.example tag 17-slim floats, while the docs recommend an exact X.Y.Z-slim tag for production. For the next major, move the checkout to its stable branch (the clone is shallow, so git fetch origin stable/18:stable/18 --depth=1 && git checkout stable/18), set TAG to the new release and repeat the pull and up. On packages, minor updates are sudo apt-get install --only-upgrade openproject followed by sudo openproject configure, which the docs say to run after every upgrade because it applies the database migrations; a major first needs the apt source switched to that version's repository, as in the install steps. Major versions go one step at a time: 16.x to 17.x is supported, 15.x to 17.x is not.

When a single server is the wrong fit

  • Past roughly 1,500 users the docs move to an external tuned database and several app servers sharing storage, or the Helm chart on Kubernetes.
  • If the team also needs Git hosting with merge requests and CI, run a forge such as self-hosted GitLab beside it; OpenProject's own repository feature is package-only and much narrower.
  • For a lighter issue tracker with a smaller stack, compare self-hosted Plane.

Questions

Is the Community edition really free for a company?

Yes. The installation guide calls it the free, no-licence edition you install on-premise, without the Enterprise add-ons. It is GPLv3, and the FAQ adds that the Community edition can have as many users as you need. Support, LDAP group sync and the other add-ons come with an Enterprise token.

Can OpenProject run on Ubuntu 24.04?

Through Docker, yes: Compose runs on any system Docker Engine supports. The DEB packages stop at Ubuntu 22.04, and the docs say OpenProject does not plan packages for newer releases such as 24.04. A packaged install whose distribution reaches end of life is moved to Compose with the documented migration guide.

Why does OpenProject redirect to https or show an SSL protocol error?

HTTPS mode is on by default, but OpenProject does not terminate TLS itself. Until a proxy handles certificates, start with OPENPROJECT_HTTPS=false as the first-login step does; in production put it back to true behind nginx and send X-Forwarded-Proto https.

Can I move a Jira Data Center instance into it?

OpenProject is building a Jira Migrator for Jira Data Center 10.x and 11.x. Its documentation marks it as beta and asks you to use it only in test setups for now, so plan a trial import on a scratch instance first.

Put OpenProject on a machine of its own

Size cores and memory from the table above, then pick a line, a city and Ubuntu or Debian in the configurator.

See the machines

Related on Server Room

Other use cases