Auto-generate SSL Certificates for Azure Container Instances

If you want to spin up a container on Azure and need to access it over HTTPS, you are probably better of running it inside an Azure App Service instead of using Azure Container Instances, as App Services support auto-generated SSL certificates out of the box. But if for some reason you want to achieve the same with Azure Container Instances, I’m going to show you how to do it in this post by using the sidecar pattern with the reverse proxy Caddy.

The Sidecar Pattern

The sidecar pattern is a common pattern used when containerizing applications. It describes the combination of the primary application container with one or more other containers used for peripheral tasks like configuration, logging or proxies. By applying this pattern, the complexity of the primary application container can be kept low, reducing the overhead that those peripheral tasks would bring when the container is used in a container orchestration environment where such tasks are usually handled by dedicated services.

A Reverse Proxy as Sidecar

A common use case for the sidecar pattern is the usage of a reverse proxy to add HTTPS capabilities to the primary application container. For our example we are going to use the web server Caddy as reverse proxy, because it supports automatic generation of SSL certificates by using Let’s Encrypt and because it is very easy to setup.

To use Caddy as a sidecar, we have to deploy a container group to Azure Container Instances. For this we are going to create a deployment file that contains both containers of the container group:

api-version: 2021-03-01
location: switzerlandnorth
name: seq
properties:
  containers:
    - name: seq
      properties:
        image: datalust/seq:latest
        ports:
          - port: 8080
            protocol: TCP
        resources:
          requests:
            cpu: 0.5
            memoryInGB: 0.5
        environmentVariables:
          - name: ACCEPT_EULA
            value: 'Y'
          - name: SEQ_API_LISTENURIS
            value: 'http://localhost:8080'
    - name: caddy
      properties:
        image: caddy:latest
        command:
          [
            'caddy',
            'reverse-proxy',
            '--from',
            'seq-with-caddy-sidecar.switzerlandnorth.azurecontainer.io',
            '--to',
            'localhost:8080',
          ]
        ports:
          - port: 80
            protocol: TCP
          - port: 443
            protocol: TCP
        resources:
          requests:
            cpu: 0.5
            memoryInGB: 0.5
  ipAddress:
    ports:
      - port: 80
        protocol: TCP
      - port: 443
        protocol: TCP
    type: Public
    dnsNameLabel: seq-with-caddy-sidecar
  osType: Linux
tags: null
type: Microsoft.ContainerInstance/containerGroups

The deployment file contains our primary application container seq as well as the sidecar container caddy.

On line 10 we specify the port on which the primary application container is listening. For our example we have to tell Seq to listen on another port than port 80, because on port 80 Caddy needs to be able to listen for HTTP traffic in order to reply to the ACME challenge done by the Let’s Encrypt server during the SSL certificate issue. If we would run the container with docker we could simply do a port mapping, or let the two containers talk to each other directly on the internal docker network. But as this is not possible with Azure Container Instances, we have to make sure our application listens on another port.

On lines 34 and 36 we specify that Caddy is listening on ports 80 and 443. Port 80 is used to reply to the ACME challenges as already mentioned, and port 443 serves all the other requests which are redirected to our primary application container. In order to make this ports accessible from the internet we also need to specify them on lines 44 and 46, otherwise they are only accessible on the host itself.

In order to put Caddy into reverse proxy mode and enable the automatic SSL certificate generation, we need to specify some startup commands on lines 26 to 31. We have to specify the mapping from the public domain name seq-with-caddy-sidecar.switzerlandnorth.azurecontainer.io to the internal URL localhost:8080.

Once we have configured everything we can deploy the container group with the following commands:

az group create --name "rg-seq-with-caddy-sidecar" --location "switzerlandnorth"

az container create --resource-group "rg-seq-with-caddy-sidecar"  --file deployment.yml

Afterwards we should be able to access the application over HTTPS on the specified domain name.