Containerized Redmine deployments still need a real database, persistent attachment storage, and a repeatable startup path. Docker Compose fits small team and lab installations because it keeps the Redmine web container, PostgreSQL database, published port, healthchecks, and named volumes in one project file.
The Compose project uses the official redmine:6.1 image with postgres:14, matching Redmine 6.1's PostgreSQL recommendation. Compose starts PostgreSQL first, waits for its healthcheck, then starts Redmine with a fixed SECRET_KEY_BASE so sessions remain valid across container recreation.
A completed installation should show both containers healthy, keep database and uploaded file data in named volumes, and return 200 OK from /login after a forced container recreate. Publish port 8080 only on a trusted host or place Redmine behind a reverse proxy and HTTPS before opening it to users.
Related: How to install Redmine on Ubuntu
Related: How to back up and restore Redmine
$ sudo install -d -m 0750 -o "$USER" -g "$USER" /srv/redmine
$ cd /srv/redmine
COMPOSE_PROJECT_NAME=redmine REDMINE_DB_PASSWORD=replace-with-a-long-random-password SECRET_KEY_BASE=replace-with-output-from-openssl-rand-hex-64
Generate fresh values before saving this file. For example, use openssl rand -base64 24 for the database password and openssl rand -hex 64 for SECRET_KEY_BASE. Changing SECRET_KEY_BASE later invalidates existing browser sessions.
$ chmod 600 .env
The file contains database and Rails session secrets. Do not commit it to a public repository or share it in support transcripts.
services: db: image: postgres:14 restart: unless-stopped environment: POSTGRES_DB: redmine POSTGRES_USER: redmine POSTGRES_PASSWORD: "${REDMINE_DB_PASSWORD}" volumes: - postgres-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready --username=$$POSTGRES_USER --dbname=$$POSTGRES_DB"] interval: 10s timeout: 5s retries: 5 redmine: image: redmine:6.1 restart: unless-stopped depends_on: db: condition: service_healthy environment: REDMINE_DB_POSTGRES: db REDMINE_DB_USERNAME: redmine REDMINE_DB_PASSWORD: "${REDMINE_DB_PASSWORD}" REDMINE_DB_DATABASE: redmine SECRET_KEY_BASE: "${SECRET_KEY_BASE}" ports: - "8080:3000" volumes: - redmine-files:/usr/src/redmine/files healthcheck: test: ["CMD", "wget", "--spider", "--quiet", "http://127.0.0.1:3000/login"] interval: 30s timeout: 10s retries: 10 start_period: 90s volumes: postgres-data: redmine-files:
redmine-files stores uploaded attachments under /usr/src/redmine/files, while postgres-data stores the database files.
Tool: Docker Compose Healthchecks Checker
$ docker compose config --services db redmine
$ docker compose up --detach --wait Network redmine_default Creating Network redmine_default Created Volume redmine_postgres-data Creating Volume redmine_postgres-data Created Volume redmine_redmine-files Creating Volume redmine_redmine-files Created Container redmine-db-1 Creating Container redmine-db-1 Created Container redmine-redmine-1 Creating Container redmine-redmine-1 Created Container redmine-db-1 Starting Container redmine-db-1 Started Container redmine-db-1 Waiting Container redmine-db-1 Healthy Container redmine-redmine-1 Starting Container redmine-redmine-1 Started Container redmine-redmine-1 Waiting Container redmine-redmine-1 Healthy
The --wait option returns only after services are running or healthy according to the Compose model.
$ docker compose ps NAME IMAGE SERVICE STATUS PORTS redmine-db-1 postgres:14 db Up 30 seconds (healthy) 5432/tcp redmine-redmine-1 redmine:6.1 redmine Up 19 seconds (healthy) 0.0.0.0:8080->3000/tcp
$ docker volume ls --filter name=redmine_ DRIVER VOLUME NAME local redmine_postgres-data local redmine_redmine-files
$ docker compose up --detach --force-recreate --wait Container redmine-db-1 Recreate Container redmine-db-1 Recreated Container redmine-redmine-1 Recreate Container redmine-redmine-1 Recreated Container redmine-db-1 Starting Container redmine-db-1 Started Container redmine-db-1 Healthy Container redmine-redmine-1 Starting Container redmine-redmine-1 Started Container redmine-redmine-1 Healthy
This restart interrupts the new Redmine instance briefly. Run it before production users create issues or upload files.
$ curl -I -sS http://127.0.0.1:8080/login HTTP/1.1 200 OK x-frame-options: SAMEORIGIN x-content-type-options: nosniff content-type: text/html; charset=utf-8 cache-control: no-store ##### snipped #####
Open http://127.0.0.1:8080/login in a browser on the host, sign in with admin and the initial admin password only long enough to set a new password, and disable or firewall the port until HTTPS is in place.
Related: How to change the Redmine admin password after first login