Secure nginx proxy server with acme-companion – Custom server (part 2)

In the last article, we learned how to set up a helper server for Docker boxes. Now, let’s make everything safe with HTTPS.

Did you miss it? Click here to read the previous post.

This time, we’ll add HTTPS to our helper server using a tool named acme-companion. Check out the repo here.

First, we need to look back at what we did last time and change the docker-compose.yaml file a bit. Let’s start!

services:
  nginx-proxy-acme:
    image: nginxproxy/acme-companion
    container_name: nginx-proxy-acme
    restart: on-failure:10
    environment:
      - DEFAULT_EMAIL=youremail@example.com
      - NGINX_PROXY_CONTAINER=nginx-proxy
    depends_on:
      - nginx-proxy
    volumes:
      - certs:/etc/nginx/certs
      - vhost:/etc/nginx/vhost.d
      - html:/usr/share/nginx/html
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - acme:/etc/acme.sh
  nginx-proxy:
    image: nginxproxy/nginx-proxy
    container_name: nginx-proxy
    restart: on-failure:10
    depends_on:
      nestjs:
          condition: service_started
    ports:
      - "80:80"
      - "443:443"  
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - certs:/etc/nginx/certs
      - vhost:/etc/nginx/vhost.d
      - html:/usr/share/nginx/html
      - ./nginx/my_proxy.conf:/etc/nginx/conf.d/my_proxy.conf:ro
  nestjs:
    image: travisnguyen20/sample-nestjs-with-docker:latest
    container_name: nestjs
    restart: on-failure:10	
    ports:
      - "3000:3000"
    depends_on:
      mysql:
        condition: service_started
    env_file:
      - .env

  mysql:
    image: mysql:8
    container_name: mysql
    restart: always
    ports:
      - "3306:3306"
    env_file:
      - .env.mysql
    volumes:
      - ./mysql/conf.d:/etc/mysql/conf.d
      - mysql-data:/var/lib/mysql 
          
volumes:
  certs:
  vhost:
  html:
  acme:
  mysql-data:

Update .env file to hint nginx-proxy-acme generate certs for it.

VIRTUAL_HOST=api.yourserver.com
VIRTUAL_PORT=3000
LETSENCRYPT_HOST=api.travisbytes.com

DATABASE_URL=yoursecret
OTHER_VAR=secret

nginx-proxy need to know which service generates certs for virtual hosts so remember to set NGINX_PROXY_CONTAINER=nginx-proxy

Each minute, nginx-proxy-acme will scan containers that have variable LETSENCRYPT_HOST set and generate certs for it and store in volume certs. nginx-proxy will use this cert to secure connections to the docker container

Make sure you point your domain to the server IP before starting the service otherwise no certificate is generated

Run this command to deploy new services

docker compose up -d

Output:

[+] Running 8/8
  nginx-proxy-acme 7 layers [⣿⣿⣿⣿⣿⣿⣿]      0B/0B      Pulled                                                                                                                 7.6s 
    4abcf2066143 Already exists                                                                                                                                              0.0s 
    c58472a96df6 Pull complete                                                                                                                                               1.4s 
    e0fd6dc3ac4d Pull complete                                                                                                                                               0.8s 
    8107d52fa040 Pull complete                                                                                                                                               0.8s 
    86858656e858 Pull complete                                                                                                                                               1.6s 
    3d2ba187a5bb Pull complete                                                                                                                                               1.9s 
    4f4fb700ef54 Pull complete                                                                                                                                               2.2s 
[+] Running 4/6
  Volume "home_certs"         Created                                                                                                                                        1.7s 
  Volume "home_acme"          Created                                                                                                                                        1.7s 
  Container mysql             Running                                                                                                                                        0.0s 
  Container nestjs            Running                                                                                                                                        0.0s 
  Container nginx-proxy       Started                                                                                                                                        1.2s 
  Container nginx-proxy-acme  Started 

To debug if the certificate is generated. Checkout ngnix-proxy-acme logs using this command

docker compose logs nginx-proxy-acme

If you see these lines that means a certificate is generated for your site.

nginx-proxy-acme  | Reloading nginx proxy (nginx-proxy)...
nginx-proxy-acme  | 2024/04/02 16:07:41 Generated '/etc/nginx/conf.d/default.conf' from 4 containers
nginx-proxy-acme  | 2024/04/02 16:07:41 [notice] 66#66: signal process started
nginx-proxy-acme  | Creating/renewal demo.travisbytes.com certificates... (demo.travisbytes.com)
nginx-proxy-acme  | [Tue Apr  2 16:07:42 UTC 2024] Using CA: https://acme-v02.api.letsencrypt.org/directory
nginx-proxy-acme  | [Tue Apr  2 16:07:42 UTC 2024] Creating domain key
nginx-proxy-acme  | [Tue Apr  2 16:07:43 UTC 2024] The domain key is here: /etc/acme.sh/travisnguyen.me@gmail.com/demo.travisbytes.com/demo.travisbytes.com.key
nginx-proxy-acme  | [Tue Apr  2 16:07:43 UTC 2024] Generate next pre-generate key.
nginx-proxy-acme  | [Tue Apr  2 16:07:43 UTC 2024] Single domain='demo.travisbytes.com'
nginx-proxy-acme  | [Tue Apr  2 16:07:43 UTC 2024] Getting domain auth token for each domain
nginx-proxy-acme  | [Tue Apr  2 16:07:46 UTC 2024] Getting webroot for domain='demo.travisbytes.com'
nginx-proxy-acme  | [Tue Apr  2 16:07:46 UTC 2024] Verifying: demo.travisbytes.com
nginx-proxy-acme  | [Tue Apr  2 16:07:47 UTC 2024] Pending, The CA is processing your order, please just wait. (1/30)
nginx-proxy-acme  | [Tue Apr  2 16:07:50 UTC 2024] Success
nginx-proxy-acme  | [Tue Apr  2 16:07:50 UTC 2024] Verify finished, start to sign.
nginx-proxy-acme  | [Tue Apr  2 16:07:50 UTC 2024] Lets finalize the order.
nginx-proxy-acme  | [Tue Apr  2 16:07:50 UTC 2024] Le_OrderFinalize='https://acme-v02.api.letsencrypt.org/acme/finalize/1650520217/257543242427'
nginx-proxy-acme  | [Tue Apr  2 16:07:53 UTC 2024] Downloading cert.
nginx-proxy-acme  | [Tue Apr  2 16:07:53 UTC 2024] Le_LinkCert='https://acme-v02.api.letsencrypt.org/acme/cert/044f162ffee3fe17ace831853bc8ea46de0b'
nginx-proxy-acme  | [Tue Apr  2 16:07:54 UTC 2024] Cert success.

Checkout your website if you can access it via https://yourdomain.com/

Awesome! Now you can generate https for any services with just a few changes in docker-compose.yaml file.