├── .env.example ├── .gitignore ├── README.md ├── control ├── Dockerfile ├── README.md ├── backup │ └── entrypoint.sh └── upgrade │ ├── entrypoint.sh │ └── scripts │ └── 00-db-upgrade.sh ├── docker-compose.control.yml ├── docker-compose.yml └── proxy ├── Caddyfile.template └── Dockerfile /.env.example: -------------------------------------------------------------------------------- 1 | ## 2 | # All environment variables defined here will only apply if you pass them 3 | # to the OpenProject container in docker-compose.yml under x-op-app -> environment. 4 | # For the examples here this is already the case. 5 | # 6 | # Please refer to our documentation to see all possible variables: 7 | # https://www.openproject.org/docs/installation-and-operations/configuration/environment/ 8 | # 9 | TAG=15-slim 10 | OPENPROJECT_HTTPS=false 11 | OPENPROJECT_HOST__NAME=localhost 12 | PORT=127.0.0.1:8080 13 | OPENPROJECT_RAILS__RELATIVE__URL__ROOT= 14 | IMAP_ENABLED=false 15 | DATABASE_URL=postgres://postgres:p4ssw0rd@db/openproject?pool=20&encoding=unicode&reconnect=true 16 | RAILS_MIN_THREADS=4 17 | RAILS_MAX_THREADS=16 18 | PGDATA="/var/lib/postgresql/data" 19 | OPDATA="/var/openproject/assets" 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | 3 | docker-compose.override.yml 4 | backups/ 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenProject installation with Docker Compose 2 | 3 | This repository contains the installation method for OpenProject using Docker Compose. 4 | 5 | 6 | > [!NOTE] 7 | > Looking for the Kubernetes installation method? 8 | > Please use the [OpenProject helm chart](https://charts.openproject.org) to install OpenProject on kubernetes. 9 | 10 | ## Quick start 11 | 12 | First, you must clone the [openproject-docker-compose](https://github.com/opf/openproject-docker-compose) repository: 13 | 14 | ```shell 15 | git clone https://github.com/opf/openproject-docker-compose.git --depth=1 --branch=stable/16 openproject 16 | ``` 17 | 18 | Copy the example `.env` file and edit any values you want to change: 19 | 20 | ```shell 21 | cp .env.example .env 22 | vim .env 23 | ``` 24 | 25 | If you are using the default value of OPDATA that is used in the ```.env.example``` you need to make sure that the folder exist, and you have the right permissions: 26 | 27 | ```shell 28 | sudo mkdir -p /var/openproject/assets 29 | sudo chown 1000:1000 -R /var/openproject/assets 30 | ``` 31 | 32 | Next you start up the containers in the background while making sure to pull the latest versions of all used images. 33 | 34 | ```shell 35 | OPENPROJECT_HTTPS=false docker compose up -d --build --pull always 36 | ``` 37 | 38 | After a while, OpenProject should be up and running on `http://localhost:8080`. The default username and password is login: `admin`, and password: `admin`. 39 | The `OPENPROJECT_HTTPS=false` environment variable explicitly disables HTTPS mode for the first startup. Without this, OpenProject assumes it's running behind HTTPS in production by default. 40 | We do strongly recommend you use OpenProject behind a TLS terminated proxy for production purposes and remove this flag before actually starting to use it. 41 | 42 | ### Customization 43 | 44 | The `docker-compose.yml` file present in the repository can be adjusted to your convenience. But note that with each pull, it will be overwritten. 45 | Best practice is to use the file `docker-compose.override.yml` for that case. 46 | For instance you could mount specific configuration files, override environment variables, or switch off services you don't need. 47 | 48 | Please refer to the official [Docker Compose documentation](https://docs.docker.com/compose/extends/) for more details. 49 | 50 | ### Troubleshooting 51 | 52 | **pull access denied for openproject/proxy, repository does not exist or may require 'docker login': denied: requested access to the resource is denied** 53 | 54 | If you encounter this after `docker compose up` this is merely a warning which can be ignored. 55 | 56 | If this happens during `docker compose pull` this is simply a warning as well. 57 | But it will result in the command's exit code to be a failure even though all images are pulled. 58 | To prevent this you can add the `--ignore-buildable` option, running `docker compose pull --ignore-buildable`. 59 | 60 | ### HTTPS/SSL 61 | 62 | By default OpenProject starts with the HTTPS option **enabled**, but it **does not** handle SSL termination itself. This 63 | is usually done separately via a [reverse proxy 64 | setup](https://www.openproject.org/docs/installation-and-operations/installation/docker/#apache-reverse-proxy-setup). 65 | Without this you will run into an `ERR_SSL_PROTOCOL_ERROR` when accessing OpenProject. 66 | 67 | See below how to disable HTTPS. 68 | 69 | Be aware that if you want to use the integrated Caddy proxy as a proxy with outbound connections, you need to rewrite the 70 | `Caddyfile`. In the default state, it is configured to forward the `X-Forwarded-*` headers from the reverse proxy in 71 | front of it and not setting them itself. This is considered a security flaw and should instead be solved by configuring 72 | `trusted_proxies` inside the `Caddyfile`. For more information read 73 | the [Caddy documentation](https://caddyserver.com/docs/caddyfile/directives/reverse_proxy). 74 | 75 | ### PORT 76 | 77 | By default the port is bound to `0.0.0.0` means access to OpenProject will be public. 78 | See below how to change that. 79 | 80 | ## Image configuration 81 | 82 | OpenProject publishes `slim` containers that you should be using for this compose setup. 83 | Please see https://www.openproject.org/docs/installation-and-operations/installation/docker/#available-containers for more information on the containers and versions we push. 84 | 85 | ## Configuration 86 | 87 | Environment variables can be added to `docker-compose.yml` under `x-op-app -> environment` to change 88 | OpenProject's configuration. Some are already defined and can be changed via the environment. 89 | 90 | You can pass those variables directly when starting the stack as follows. 91 | 92 | ``` 93 | VARIABLE=value docker-compose up -d 94 | ``` 95 | 96 | You can also put those variables into an `.env` file in your current working 97 | directory, and Docker Compose will pick it up automatically. See `.env.example` 98 | for details. 99 | 100 | ## HTTPS 101 | 102 | You can disable OpenProject's HTTPS option via: 103 | 104 | ``` 105 | OPENPROJECT_HTTPS=false 106 | ``` 107 | 108 | ## PORT 109 | 110 | If you want to specify a different port, you can do so with: 111 | 112 | ``` 113 | PORT=4000 114 | ``` 115 | 116 | If you don't want OpenProject to bind to `0.0.0.0` you can bind it to localhost only like this: 117 | 118 | ``` 119 | PORT=127.0.0.1:8080 120 | ``` 121 | 122 | ## TAG 123 | 124 | If you want to specify a custom tag for the OpenProject docker image, you can do so with: 125 | 126 | ``` 127 | TAG=my-docker-tag 128 | ``` 129 | 130 | ## BIM edition 131 | 132 | In order to install or change to BIM inside a Docker environment, please navigate to the [Docker Installation for OpenProject BIM](https://www.openproject.org/docs/installation-and-operations/bim-edition/#docker-installation-openproject-bim) paragraph at the BIM edition documentation. 133 | 134 | ## Upgrade 135 | 136 | Retrieve any changes from the `openproject-docker-compose` repository: 137 | 138 | git pull origin stable/16 139 | 140 | Build the control plane: 141 | 142 | docker-compose -f docker-compose.yml -f docker-compose.control.yml build 143 | 144 | Take a backup of your existing postgresql data and openproject assets: 145 | 146 | docker-compose -f docker-compose.yml -f docker-compose.control.yml run backup 147 | 148 | Run the upgrade: 149 | 150 | docker-compose -f docker-compose.yml -f docker-compose.control.yml run upgrade 151 | 152 | Relaunch the containers, ensure you are pulling to use the latest version of the Docker images: 153 | 154 | docker compose up -d --build --pull always 155 | 156 | 157 | 158 | ## Backup 159 | 160 | Switch off your current installation: 161 | 162 | docker-compose down 163 | 164 | Build the control scripts: 165 | 166 | docker-compose -f docker-compose.yml -f docker-compose.control.yml build 167 | 168 | Take a backup of your existing PostgreSQL data and OpenProject assets: 169 | 170 | docker-compose -f docker-compose.yml -f docker-compose.control.yml run backup 171 | 172 | Restart your OpenProject installation 173 | 174 | docker-compose up -d 175 | 176 | 177 | 178 | ## Uninstall 179 | 180 | If you want to stop the containers without removing them directly: 181 | 182 | ```bash 183 | docker-compose stop 184 | ``` 185 | 186 | You can remove the container stack with: 187 | 188 | ```bash 189 | docker-compose down 190 | ``` 191 | 192 | > [!NOTE] 193 | > This will not remove your data which is persisted in named volumes, likely called `compose_opdata` (for attachments) and `compose_pgdata` (for the database). 194 | > The exact name depends on the name of the directory where your `docker-compose.yml` and/or you `docker-compose.override.yml` files are stored (`compose` in this case). 195 | 196 | If you want to start from scratch and remove the existing data you will have to remove these volumes via 197 | `docker volume rm compose_opdata compose_pgdata`. 198 | 199 | ## Troubleshooting 200 | 201 | You can look at the logs with: 202 | 203 | docker-compose logs -n 1000 204 | 205 | For the complete documentation, please refer to https://docs.openproject.org/installation-and-operations/. 206 | 207 | ### Network issues 208 | 209 | If you're running into weird network issues and timeouts such as the one described in 210 | [OP#42802](https://community.openproject.org/work_packages/42802), you might have success in remove the two separate 211 | frontend and backend networks. This might be connected to using podman for orchestration, although we haven't been able 212 | to confirm this. 213 | 214 | ### SMTP setup fails: Network is unreachable. 215 | 216 | Make sure your container has DNS resolution to access external SMTP server when set up as described in 217 | [OP#44515](https://community.openproject.org/work_packages/44515). 218 | 219 | ```yml 220 | worker: 221 | dns: 222 | - "Your DNS IP" # OR add a public DNS resolver like 8.8.8.8 223 | ``` 224 | -------------------------------------------------------------------------------- /control/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:12 2 | 3 | RUN apt-get update -qq && apt-get install wget gnupg2 -y && rm -rf /var/lib/apt/lists/* 4 | RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - 5 | RUN echo "deb http://apt.postgresql.org/pub/repos/apt bookworm-pgdg main" > /etc/apt/sources.list.d/pgdg.list 6 | RUN apt-get update -qq && apt-get install postgresql-9.6 postgresql-10 postgresql-13 -y && rm -rf /var/lib/apt/lists/* 7 | RUN localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 8 | 9 | ENV LANG en_US.utf8 10 | 11 | ADD . /control 12 | -------------------------------------------------------------------------------- /control/README.md: -------------------------------------------------------------------------------- 1 | # Control your OpenProject installation 2 | 3 | ## Backup 4 | 5 | Switch off your current installation: 6 | ```shell 7 | docker-compose down 8 | ```` 9 | Build the control scripts: 10 | ```shell 11 | docker-compose -f docker-compose.yml -f docker-compose.control.yml build 12 | ``` 13 | Take a backup of your existing PostgreSQL data and OpenProject assets: 14 | ```shell 15 | docker-compose -f docker-compose.yml -f docker-compose.control.yml run backup 16 | ``` 17 | Restart your OpenProject installation 18 | ```shell 19 | docker-compose up -d 20 | ```` 21 | ## Upgrade 22 | 23 | Switch off your current installation (using the outdated postgres engine): 24 | ```shell 25 | docker-compose down 26 | ``` 27 | Fetch the latest changes from this repository: 28 | ```shell 29 | git pull origin stable/15 # adjust if needed 30 | ``` 31 | Build the control plane: 32 | ```shell 33 | docker-compose -f docker-compose.yml -f docker-compose.control.yml build 34 | ``` 35 | Take a backup of your existing postgresql data and openproject assets: 36 | ```shell 37 | docker-compose -f docker-compose.yml -f docker-compose.control.yml run backup 38 | ``` 39 | Run the upgrade: 40 | ```shell 41 | docker-compose -f docker-compose.yml -f docker-compose.control.yml run upgrade 42 | ``` 43 | Relaunch your OpenProject installation, using the normal Compose command: 44 | ```shell 45 | docker-compose up -d 46 | ``` 47 | Test that everything works again, the database container should now be running postgres 17. 48 | -------------------------------------------------------------------------------- /control/backup/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | timestamp=$(date +%s) 5 | mkdir -p /backups 6 | cd /backups 7 | filename="${timestamp}-pgdata.tar.gz" 8 | echo "Backing up PostgreSQL data into backups/${filename}..." 9 | tar czf "${filename}" -C "$PGDATA" . 10 | filename="${timestamp}-opdata.tar.gz" 11 | echo "Backing up OpenProject assets into backups/${filename}..." 12 | tar czf "${filename}" -C "$OPDATA" . 13 | echo "DONE" 14 | -------------------------------------------------------------------------------- /control/upgrade/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | set -o pipefail 4 | 5 | /control/upgrade/scripts/00-db-upgrade.sh 6 | 7 | echo "Please restart your installation by issuing the following command:" 8 | echo " docker compose up -d --build --pull always" 9 | -------------------------------------------------------------------------------- /control/upgrade/scripts/00-db-upgrade.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | set -o pipefail 4 | 5 | CURRENT_PGVERSION="$(cat $PGDATA/PG_VERSION)" 6 | NEW_PGVERSION="13" 7 | PGWORKDIR=${PGWORKDIR:=/var/lib/postgresql/work} 8 | 9 | if [ ! "$CURRENT_PGVERSION" -lt "$NEW_PGVERSION" ]; then 10 | echo "Current PG version is higher or equal to the PG version to be installed ($CURRENT_PGVERSION > $NEW_PGVERSION). Ignoring." 11 | exit 0 12 | fi 13 | 14 | export PGBINOLD="/usr/lib/postgresql/$CURRENT_PGVERSION/bin" 15 | export PGBINNEW="/usr/lib/postgresql/$NEW_PGVERSION/bin" 16 | export PGDATAOLD="$PGDATA" 17 | export PGDATANEW="$PGWORKDIR/datanew" 18 | 19 | rm -rf "$PGWORKDIR" && mkdir -p "$PGWORKDIR" "$PGDATANEW" 20 | chown -R postgres.postgres "$PGDATA" "$PGWORKDIR" 21 | cd "$PGWORKDIR" 22 | # initialize new db 23 | su -m postgres -c "$PGBINNEW/initdb --pgdata=$PGDATANEW --encoding=unicode --auth=trust" 24 | echo "Performing a dry-run migration to PostgreSQL $NEW_PGVERSION..." 25 | su -m postgres -c "$PGBINNEW/pg_upgrade -c" 26 | echo "Performing the real migration to PostgreSQL $NEW_VERSION..." 27 | su -m postgres -c "$PGBINNEW/pg_upgrade" 28 | su -m postgres -c "rm -rf $PGDATAOLD/* && mv $PGDATANEW/* $PGDATAOLD/" 29 | # as per docker hub documentation 30 | su -m postgres -c "echo \"listen_addresses = '*'\" >> $PGDATAOLD/postgresql.conf" 31 | su -m postgres -c "echo \"host all all all md5\" >> $PGDATAOLD/pg_hba.conf" 32 | echo "DONE" 33 | -------------------------------------------------------------------------------- /docker-compose.control.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | 3 | volumes: 4 | pgupgrade: 5 | 6 | services: 7 | db: 8 | restart: "no" 9 | entrypoint: ["echo", "disabled"] 10 | upgrade: 11 | restart: "no" 12 | build: 13 | context: ./control 14 | environment: 15 | PGDATA: /var/lib/postgresql/data 16 | volumes: 17 | - "${PGDATA:-pgdata}:/var/lib/postgresql/data" 18 | - "./control:/control" 19 | entrypoint: ["/control/upgrade/entrypoint.sh"] 20 | backup: 21 | restart: "no" 22 | build: 23 | context: ./control 24 | environment: 25 | PGDATA: /var/lib/postgresql/data 26 | OPDATA: /var/openproject/assets 27 | volumes: 28 | - "${PGDATA:-pgdata}:/var/lib/postgresql/data" 29 | - "${OPDATA:-opdata}:/var/openproject/assets" 30 | - "./backups:/backups" 31 | - "./control:/control" 32 | entrypoint: ["/control/backup/entrypoint.sh"] 33 | web: 34 | restart: "no" 35 | entrypoint: ["echo", "disabled"] 36 | worker: 37 | restart: "no" 38 | entrypoint: ["echo", "disabled"] 39 | cron: 40 | restart: "no" 41 | entrypoint: ["echo", "disabled"] 42 | seeder: 43 | restart: "no" 44 | entrypoint: ["echo", "disabled"] 45 | proxy: 46 | restart: "no" 47 | entrypoint: ["echo", "disabled"] 48 | cache: 49 | restart: "no" 50 | entrypoint: ["echo", "disabled"] 51 | 52 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | networks: 2 | frontend: 3 | backend: 4 | 5 | volumes: 6 | pgdata: 7 | opdata: 8 | 9 | x-op-restart-policy: &restart_policy 10 | restart: unless-stopped 11 | x-op-image: &image 12 | image: openproject/openproject:${TAG:-16-slim} 13 | x-op-app: &app 14 | <<: [*image, *restart_policy] 15 | environment: 16 | OPENPROJECT_HTTPS: "${OPENPROJECT_HTTPS:-true}" 17 | OPENPROJECT_HOST__NAME: "${OPENPROJECT_HOST__NAME:-localhost:8080}" 18 | OPENPROJECT_HSTS: "${OPENPROJECT_HSTS:-true}" 19 | RAILS_CACHE_STORE: "memcache" 20 | OPENPROJECT_CACHE__MEMCACHE__SERVER: "cache:11211" 21 | OPENPROJECT_RAILS__RELATIVE__URL__ROOT: "${OPENPROJECT_RAILS__RELATIVE__URL__ROOT:-}" 22 | DATABASE_URL: "${DATABASE_URL:-postgres://postgres:p4ssw0rd@db/openproject?pool=20&encoding=unicode&reconnect=true}" 23 | RAILS_MIN_THREADS: ${RAILS_MIN_THREADS:-4} 24 | RAILS_MAX_THREADS: ${RAILS_MAX_THREADS:-16} 25 | # set to true to enable the email receiving feature. See ./docker/cron for more options 26 | IMAP_ENABLED: "${IMAP_ENABLED:-false}" 27 | volumes: 28 | - "${OPDATA:-opdata}:/var/openproject/assets" 29 | 30 | services: 31 | db: 32 | image: postgres:13 33 | <<: *restart_policy 34 | stop_grace_period: "3s" 35 | volumes: 36 | - "${PGDATA:-pgdata}:/var/lib/postgresql/data" 37 | environment: 38 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-p4ssw0rd} 39 | POSTGRES_DB: openproject 40 | networks: 41 | - backend 42 | 43 | cache: 44 | image: memcached 45 | <<: *restart_policy 46 | networks: 47 | - backend 48 | 49 | proxy: 50 | build: 51 | context: ./proxy 52 | args: 53 | APP_HOST: web 54 | image: openproject/proxy 55 | <<: *restart_policy 56 | ports: 57 | - "${PORT:-8080}:80" 58 | depends_on: 59 | - web 60 | networks: 61 | - frontend 62 | 63 | web: 64 | <<: *app 65 | command: "./docker/prod/web" 66 | networks: 67 | - frontend 68 | - backend 69 | depends_on: 70 | - db 71 | - cache 72 | - seeder 73 | labels: 74 | - autoheal=true 75 | healthcheck: 76 | test: ["CMD", "curl", "-f", "http://localhost:8080${OPENPROJECT_RAILS__RELATIVE__URL__ROOT:-}/health_checks/default"] 77 | interval: 10s 78 | timeout: 3s 79 | retries: 3 80 | start_period: 30s 81 | 82 | autoheal: 83 | image: willfarrell/autoheal:1.2.0 84 | volumes: 85 | - "/var/run/docker.sock:/var/run/docker.sock" 86 | environment: 87 | AUTOHEAL_CONTAINER_LABEL: autoheal 88 | AUTOHEAL_START_PERIOD: 600 89 | AUTOHEAL_INTERVAL: 30 90 | 91 | worker: 92 | <<: *app 93 | command: "./docker/prod/worker" 94 | networks: 95 | - backend 96 | depends_on: 97 | - db 98 | - cache 99 | - seeder 100 | 101 | cron: 102 | <<: *app 103 | command: "./docker/prod/cron" 104 | networks: 105 | - backend 106 | depends_on: 107 | - db 108 | - cache 109 | - seeder 110 | 111 | seeder: 112 | <<: *app 113 | command: "./docker/prod/seeder" 114 | restart: on-failure 115 | networks: 116 | - backend 117 | -------------------------------------------------------------------------------- /proxy/Caddyfile.template: -------------------------------------------------------------------------------- 1 | :80 { 2 | reverse_proxy * http://${APP_HOST}:8080 { 3 | # The following directives are needed to make the proxy forward explicitly the X-Forwarded-* headers. If unset, 4 | # Caddy will reset them. See: https://caddyserver.com/docs/caddyfile/directives/reverse_proxy#defaults 5 | # This is needed, if you are using a reverse proxy in front of the compose stack and Caddy is NOT your first 6 | # point of contact. 7 | # When using Caddy is reachable as a first point of contact, it is highly recommended to configure the server's 8 | # global `trusted_proxies` directive. See: https://caddyserver.com/docs/caddyfile/options#trusted-proxies 9 | 10 | header_up X-Forwarded-Proto {header.X-Forwarded-Proto} 11 | header_up X-Forwarded-For {header.X-Forwarded-For} 12 | } 13 | 14 | file_server 15 | 16 | log 17 | } 18 | -------------------------------------------------------------------------------- /proxy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM caddy:2 2 | 3 | COPY ./Caddyfile.template /etc/caddy/Caddyfile.template 4 | 5 | ARG APP_HOST 6 | RUN sed 's|${APP_HOST}|'"$APP_HOST"'|g' /etc/caddy/Caddyfile.template > /etc/caddy/Caddyfile 7 | 8 | ENTRYPOINT ["caddy", "run", "--config", "/etc/caddy/Caddyfile"] 9 | --------------------------------------------------------------------------------