Install Redmine on a VPS using Redmine, MySQL and Nginx Docker containers

By Atif

December 6, 2025

In this guide you will learn how to install Redmine on a VPS using Docker and connect to your domain using Nginx as reverse-proxy. I will use Cloudflare’s origin certificate to setup SSL because my domain is already setup with Cloudflare but you can also use Certbot container to generate certificate and use crontab to automatically new it.

Why install Redmine in a container?

As a web developer I have multiple side projects and development servers running on multiple VPS and most of these VPS are sitting idle all the time. I am paying for resources I am not using. Only if I could host multiple projects on one or at least fewer VPS saving a lot of money in monthly VPS expense. You see you create a web server by installing the required stack and then you want to keep it as stable as possible by not installing a bunch of other unrelated things. If you have LEMP stack running for PHP projects you avoid installing stuff to run node or python or ruby projects.

My old Redmine installation running on Ubuntu 14 was having issues and I wanted to create a new one, but the problem is that unlike before no VPS provider is offering one-click image for the Redmine so you have to install and set it up all by yourself. If you want to create a Redmine installation on a VPS without docker you can follow the this guide here or here. But it’s a time taking process and you don’t want to do it on an existing VPS fearing it might interfere and break other existing apps or projects.

There is no proper guide I could find to follow and install Redmine using docker containers and configure it to serve using my domain. If you are a docker expert you might be able to follow the documentation on Redmine docker image here, but it looked outdated and wasn’t enough for me. So I went down the rabbit hole and spent few days researching and trying out things and here is the fruit of all that effort.

Here is all the meat

If you already know docker well and don’t want to learn and understand but just want to copy/paste and get going for now, here are the commands you can run one by one to install and setup Redmine with Docker.

docker network create my-network

docker run --name nginx --network my-network -v ./nginx/conf:/etc/nginx/conf.d/ -v ./data/nginx/ssl:/etc/nginx/ssl -p 80:80 -p 443:443 -d nginx:1.29

docker run --name redmine-mysql --network my-network -v ./redmine/mysql:/var/lib/mysql -e MYSQL_USER=redmine -e MYSQL_PASSWORD=wBH*46wrqnz5En -e MYSQL_DATABASE=redmine -e MYSQL_RANDOM_ROOT_PASSWORD=1 -d mysql:8.4.7

docker run --name redmine --network my-network -v ./redmine/files:/usr/src/redmine/files -v ./redmine/config/configuration.yml:/usr/src/redmine/config/configuration.yml -e REDMINE_DB_MYSQL=redmine-mysql -e REDMINE_DB_USERNAME=redmine -e REDMINE_DB_PASSWORD=wBH*46wrqnz5En -d redmine:6.1.0

Docker Compose

Here is the file If you prefer Docker Compose

services:
  redmine-mysql:
    image: mysql:8.4.7
    restart: always
    environment:      
      MYSQL_USER: redmine
      MYSQL_PASSWORD: wBtLHgP*l@qnz5En
      MYSQL_DATABASE: redmine
      # MYSQL_ROOT_PASSWORD: redmine
      MYSQL_RANDOM_ROOT_PASSWORD: 1
    volumes:
      - .\data\mysql:/var/lib/mysql
    
  redmine:
    image: redmine:6.1.0
    restart: always
    ports:
      - "3000:3000"
    environment:
      REDMINE_DB_MYSQL: redmine-mysql
      REDMINE_DB_DATABASE: redmine
      REDMINE_DB_USERNAME: redmine
      REDMINE_DB_PASSWORD: wBtLHgP*l@qnz5En
    volumes:
      - ./data/redmine/files:/usr/src/redmine/files
      - ./data/redmine/config/configuration.yml:/usr/src/redmine/config/configuration.yml
    depends_on:
      - redmine-mysql

  nginx:
    image: nginx:1.29
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./data/nginx/conf:/etc/nginx/conf.d:ro
      - ./data/nginx/ssl:/etc/nginx/ssl:ro
    depends_on:
      - redmine    

To learn and understand how it’s working read the detailed step by step guide below.

Create a Network

First of all you create a network for the containers if you already don’t have one so they can communicate with each other. We will use this network later when creating Redmine, MySQL and Nginx containers so they can talk to each other.

docker network create my-network

Create Containers

Now we are going to create containers starting with Nginx container.

Nginx container

Now we are going to create the Nginx container using this command.

Note: You need to create the Nginx default.conf file mentioned below before you execute this command so your nginx configuration is copied over to the nginx container.

docker run --name nginx --network my-network -v ./nginx/conf:/etc/nginx/conf.d/ -p 80:80 -d nginx:1.29

What it means

--name nginx sets the name of the container as nginx.

--network my-network tells it to use my-network as network to communicate with other containers on the same network.

-v ./nginx/conf:/etc/nginx/conf.d/ is used to declare a local volume to save and sync data between the created volume and the path provided in the container.

./nginx/ before the colon : is path of the local volume relative to the directory where you are executing the docker command.

/etc/nginx/conf.d/ after the colon : is the path of the nginx configuration files on the container.

Here were are creating a volume for the Nginx configuration that we want to use to serve our apps on this network. When the container is created it will copy the .conf file in the ./nginx/ directory to the /etc/nginx/conf.d/ directory.

If this container is deleted or recreated we don’t need to worry about backing up and rewriting the configuration files.

Here is a sample default.conf file for Redmine Nginx configuration. Create this file before creating the container.

server {
    listen       80;
    listen  [::]:80;
    server_name  redmine.your-domain.com;
    
    location / {
        proxy_pass   http://redmine:3000;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    #access_log  /var/log/nginx/host.access.log  main;
    error_log /etc/nginx/conf.d/log/redmine.log;

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

-p maps the container port to the local

-d is used to run the command in detached mode meaning it will now show use the output and run the command in the background leaving the terminal free for us to use.

nginx:1.29 tells it to create the container using this image. nginx is the image name and 1.29 after the colon : is the nginx version number. In order to avoid any breaking changes use a specific version tag instead of the latest tag.

Now if you have already configured the DNS and pointed your domain to the VPS you will see the default Nginx welcome page when you hit the domain URL.

MySQL Container

Use the following command to create the MySQL container

docker run --name redmine-mysql --network my-network -v ./redmine/mysql:/var/lib/mysql -e MYSQL_USER=redmine -e MYSQL_PASSWORD=f5e@45sdf5eYN% -e MYSQL_DATABASE=redmine -e MYSQL_RANDOM_ROOT_PASSWORD=1 -d mysql:8.4.7

What it means

-v ./redmine/mysql:/var/lib/mysql we are creating a local volume to save and sync the database data

-e MYSQL_USER=redmine -e MYSQL_PASSWORD=f5e@45sdf5eYN% -e MYSQL_DATABASE=redmine -e MYSQL_RANDOM_ROOT_PASSWORD=1 are environment variables we pass on to the container to create a database and it’s user and password. You see details and other variables by visiting the MySQL image page on the docker hub here.

Rest of them self explanatory if you read the details of the nignx container command.

Redmine Container

Use the following command to create the Redmine container

docker run --name redmine --network my-network -v ./redmine/files:/usr/src/redmine/files -v ./redmine/config/configuration.yml:/usr/src/redmine/config/configuration.yml -e REDMINE_DB_MYSQL=redmine-mysql -e REDMINE_DB_USERNAME=redmine -e REDMINE_DB_PASSWORD=f5e@45sdf5eYN% -d redmine:6.1.0

What it means

-e REDMINE_DB_MYSQL=redmine-mysql environment variable tells the Redmine to use redmine-mysql container as hour db host.

-v ./redmine/files:/usr/src/redmine/files creates a local volume to save and sync the files you add to Redmine.

v ./redmine/config/configuration.yml:/usr/src/redmine/config/configuration.yml creates a volume to save and sync the Redmine configuration file. This file is used to change and setup different Redmine settings for example the Emails.

I used google workspace email and configured the smtp settings like below.

email_delivery:
  delivery_method: :smtp
  smtp_settings:
    enable_starttls_auto: true
    address: "smtp.gmail.com"
    port: 587
    domain: "your-domain.com" # 'your.domain.com' for GoogleApps
    authentication: :plain
    user_name: "[email protected]"
    password: "your-google-app-password"

Create Origin Certificates on Cloudflare and configure SSL in Nginx container

Use the following guide to create Origin certificate on Cloudflare if your domain is already setup with Cloudflare.
How to use Cloudflare’s free origin certificate to secure your website with SSL

Use the following guide to create certificates with Certbot and setup auto renewal
How to create SSL certificates using Certbot and setup auto renewal

Copy the certificate.pem and private.key to the nginx/ssl folder.

Once you have copied the certificate and the key, you need to update your default.conf file to accept and serve secure requests over HTTPS using the generated SSL certificate and key.

Here is an example default.conf file below.

server {
    listen       80;
    listen  [::]:80;
    server_name  redmine.your-domain.com;

    # Redirect all HTTP traffic permanently (301) to the HTTPS version
    return 301 https://$host$request_uri;
    error_log /etc/nginx/conf.d/log/redmine.log;
}
server {
    listen 443 ssl;
    server_name  redmine.your-domain.com;

    ssl_certificate /etc/nginx/ssl/certificate.pem;  # Your Origin Certificate
    ssl_certificate_key /etc/nginx/ssl/private.key; # Your Private Key

    # Standard SSL settings (improves security)
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256';
    ssl_prefer_server_ciphers off;

    location / {
        proxy_pass   http://redmine:3000;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    #access_log  /var/log/nginx/host.access.log  main;
    error_log /etc/nginx/conf.d/log/redmine.log;


    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

Now you need to stop and delete the existing Nginx container and create a new container with a new volume for the certificate and key attached, and updated Nginx configuration.

Stop existing Nginx container

docker stop nginx

Delete existing Nginx container

docker rm nginx

Create new Nginx container with update Nginx configuration and volume for SSL Certificates

docker run --name nginx --network my-network -v ./nginx/conf:/etc/nginx/conf.d/ -v ./data/nginx/ssl:/etc/nginx/ssl -p 80:80 -p 443:443 -d nginx:1.29

Feel free to ask me in comments if you have any questions or see any issues.

Hey! how u doing 🙂