[Now] Let's Redmine with Docker Compose with Let's Encrypt

Thing you want to do

What I wanted to do

I also thought about wrike

It seems that the time track function cannot be used with the free plan. I'm sorry because I want to manage what I spend my time on.

I decided to use planio

The free plan does not allow you to include plugins to use tags. Paid plans are too expensive for personal use.

I thought I'd host it in the cloud

I thought about using bitnami to host it to the cloud quickly, but the running cost is as stupid. It costs several thousand yen a month even if it is assembled with the essential configuration of bitnami.

What to do

Set up a home server

It feels like it's not the case nowadays, but since the cloud () isn't in my use case, I have no choice but to maintain it myself. I bought a Raspberry Pi 4 4GB with low power consumption and space saving. It's a hassle to think about what case to use and what to do with the SD card, so I decided to buy a starter kit from Japanese distributor.

The Raspberry Pi 3 I bought a few years ago had 1GB of memory, but it still has Moore's Law.

Use Docker Compose

Nowadays it's k8s! I think, but I haven't studied at all, so I can't read the procedures and configuration files posted on the Web ... When I want to change something, my hands and legs will not come out ....

The configuration of the container should be similar to that of the reverse proxy, AP server (Redmine), and DB server (MariaDB).

I graduate from my self-signed certificate

Before I knew it, it was becoming a world where even individuals could easily obtain (!) SSL certificates for free. It is a certificate authority called Let ’s Encrypt. You will actually use an ACME client called Certbot.

What i did

Network preset

From OS installation to Docker Compose installation

Basically, there is no problem with the implementation according to other Qiita articles. The change of the network configuration file was not reflected at the first startup, so I searched for the save destination of the netplan configuration file or executed the netplan command by myself. What is netplan?

Run Redmine (finally the main subject)

Pakuri source of setting

Directory structure

The layout of directories and files is as follows.

./
├── docker-compose.yml
├── redmine
│   ├── config.ru
│   ├── plugins
│   └── themes
└── reverse_proxy
    ├── Dockerfile
    ├── default.conf.template
    └── ssl

Set up Redmine

In docker-compose.yml, the part of AP server (Redmine) is as follows.


x-DEFINE: &APP_RELATIVE_URL_ROO
  /redmine

x-DEFINE: &DB_PASSWORD
  redmine_passward

x-DEFINE: &TIME_ZONE
  Asia/Tokyo

(Omitted)

  app:
    image: redmine:latest
    container_name: app
    ports:
      - 3000:3000
    volumes:
      - ./redmine/plugins:/usr/src/redmine/plugins
      - ./redmine/themes:/usr/src/redmine/public/themes
      - ./redmine/config.ru:/usr/src/redmine/config.ru
    environment:
      TZ: *TIME_ZONE
      REDMINE_DB_MYSQL: db
      REDMINE_DB_PASSWORD: *DB_PASSWORD
      RAILS_RELATIVE_URL_ROOT: *APP_RELATIVE_URL_ROO
    depends_on:
      - db
    restart: always

Plugins and themes should be stored in the directory mapped by the volume as needed. config.ru is a configuration file that needs to be changed in order to operate a subdirectory (for example, access it at https://my.home.com/redmine). The contents after the change are as follows. The value of RAILS_RELATIVE_URL_ROOT is defined in docker-compose.yml.


# This file is used by Rack-based servers to start the application.
require ::File.expand_path('../config/environment',  __FILE__)
map ENV['RAILS_RELATIVE_URL_ROOT'] || '/' do
    run Rails.application
end

Set up MariaDB

The DB server (MariaDB) part of docker-compose.yml is as follows. There is nothing special to mention.

x-DEFINE: &DB_PASSWORD
  redmine_passward

x-DEFINE: &TIME_ZONE
  Asia/Tokyo

(Omitted)
  db:
    image: mariadb:latest
    container_name: db
    ports:
      - 3306:3306
    environment:
      MYSQL_ROOT_PASSWORD: *DB_PASSWORD
      MYSQL_DATABASE: redmine
      TZ: *TIME_ZONE
    volumes:
      - ./data/db:/var/lib/mysql
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
    restart: always

Set nginx + certbot

Set up reverse proxy (nginx) and get ssl certificate (certbot).

The reverse proxy part of docker-compose.yml is as follows.


x-DEFINE: &APP_RELATIVE_URL_ROO
  /redmine

(Abbreviation)

  reverse_proxy:
    container_name: reverse_proxy
    build:
      context: ./reverse_proxy
      network: host
      args:
        - [email protected] #replace with your own email
        - DOMAIN_LIST=my.home.com                #replace with your own domains
    environment:
      APP_ROOT: *APP_RELATIVE_URL_ROO
    volumes:
      - ./reverse_proxy/default.conf.template:/etc/nginx/templates/default.conf.template
      - ./reverse_proxy/ssl:/etc/letsencrypt
    ports:
      - "80:80"
      - "443:443"
    restart: always

The reverse proxy image is not the official nginx image, but you build your own custom one. The two parameters CERTBOT_EMAIL`` DOMAIN_LIST passed when building the image are the contact email address registered in Let's Encrypt and the domain name for which the ssl certificate is to be obtained. These values ​​are set from docker-compose.yml.

The name of the subdirectory where Redmine runs is specified by environments. volumes specifies the nginx configuration file and the storage destination for ssl authentication.

The contents of the reverse proxy Dockerfile are as follows.


FROM nginx:latest
ARG [email protected]
ARG DOMAIN_LIST

# Expose ports.
EXPOSE 80
EXPOSE 443

RUN  apt-get update \
      && apt-get upgrade -y \
      && apt-get install -y cron certbot \
      && certbot certonly --dry-run --standalone --agree-tos -m "${CERTBOT_EMAIL}" -n -d ${DOMAIN_LIST} \
#     && certbot certonly  --standalone --agree-tos -m "${CERTBOT_EMAIL}" -n -d ${DOMAIN_LIST} \
      && rm -rf /var/lib/apt/lists/* \
      && echo "@monthly /usr/local/bin/certbot renew --nginx >> /var/log/cron.log 2>&1" >/etc/cron.d/certbot-renew \
      && crontab /etc/cron.d/certbot-renew 
VOLUME /etc/letsencrypt

CMD [ "sh", "-c", "cron && ./docker-entrypoint.sh nginx -g 'daemon off;'" ]

For a detailed explanation, please refer to Pakurigen. What has changed from the original Pakuri

The contents of default.conf.template are as follows. (The https setting confirms the success of certbot once and then uncomments)


server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;

    location ${APP_ROOT} {
        proxy_set_header X-Forwarded-Host $host:$server_port;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://app:3000${APP_ROOT}/;
    }

    # --- For CertBot ---
    location ^~ /.well-known/acme-challenge/ {
        root /usr/share/nginx/html/;
    }

    location = /.well-known/acme-challenge/ {
        return 404;
    }
    # -----------------

    #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;
    }

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}

#server {
#   listen 443;
#   server_name shop.nureteni.awa.co.jp;
#
#    ssl on;
#    ssl_certificate /etc/letsencrypt/live/${MY_DOMAIN_NAME}.cert;
#    ssl_certificate_key /etc/letsencrypt/live/${MY_DOMAIN_NAME}.cert.key;
#
#    location ${APP_ROOT} {
#
#    proxy_set_header X-Forwarded-Host $host:$server_port;
#    proxy_set_header X-Forwarded-Server $host;
#    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#    proxy_pass http://app:3000${APP_ROOT}/;
#    }
#    # --- For CertBot ---
#    location ^~ /.well-known/acme-challenge/ {
#        root /usr/share/nginx/html/;
#    }
#
#    location = /.well-known/acme-challenge/ {
#        return 404;
#    }
#}

With the above, docker-compose up -d will start up the required container. After that, you can access Redmine at https://my.home.com/redmine.

in conclusion

I was often stuck in the details, and although I was doing it during the gap time, I ended up spending nearly two weeks. I think it's a minor use case, but I posted this article in the hope that it would benefit someone somewhere on the planet.

Recommended Posts

[Now] Let's Redmine with Docker Compose with Let's Encrypt
Build Rails environment with Docker Compose
WordPress with Docker Compose on CentOS 8
Create Rails 6 + MySQL environment with Docker compose
HTTPS with Spring Boot and Let's Encrypt
Try using Kong + Konga with Docker Compose.
Launched Redmine with Docker on Raspberry Pi 3
Notes on building Rails6 / PostgreSQL with Docker Compose
[Workshop for beginners] Let's write an E2E test with Cloud9 + Docker Compose + Cypress!
Maybe it works! Let's get started with Docker!
How to use docker compose with NVIDIA Jetson
Redmine on Docker
Now that you have deployed AWS with Rails On Docker, let's organize the contents.
[Introduction to Docker x ECS] ECS deployment with docker compose up
Periodically execute Python Script with Docker Compose + Daemon (cron)
Docker Compose does not start with docker.credentials.errors.InitializationError error message
Make Nginx of CentOS8 SSL compatible with Let's Encrypt
Launch MariaDB with Docker
Rails deploy with Docker
Run Pico with docker
Explode Docker with WSL2
Use Puphpeteer with Docker
Operate Emby with Docker
Try WildFly with Docker
Use ngrok with Docker
Run Payara with Docker
[Docker] Connection with MySQL
Php settings with Docker
Let's scrape with Java! !!
Getting Started with Docker
Disposable PHP with Docker
Install Composer with Docker
Let's write how to make API with SpringBoot + Docker from 0