Deploying Coder with Docker and HTTPS

author avatar
Eric Paulsen
 on January 25th, 2022
Updated on January 26th, 2022
4 min read

Encrypting network traffic with TLS has become a standard practice with more applications moving to the cloud. It is how many organizations secure their Coder instance when deploying on top of Kubernetes. This is usually accomplished by terminating TLS at the load balancer level.

However, with the release of Coder for Docker, the process to enable TLS is quite different, but just as important nonetheless. In this post, we’ll discuss how you can access your Coder for Docker deployment over HTTPS with a reverse proxy.

Understanding a reverse proxy

In some ways, a reverse proxy acts just like a regular proxy. It acts as a middleman, communicating with web servers on behalf of client machines. Yet, it comes with a few important caveats.

First, instead of masking outgoing connections, a reverse proxy masks the incoming traffic. When provided with a URL, such as coder.com, the reverse proxy will handle where the request is taken, even with a multi-server configuration.

Second, a reverse proxy enables you to run services on a variety of ports, without having to expose those ports to a user-facing network. In this case, Coder for Docker runs on port 7080. By using a reverse proxy, you only need to open ports 80 (HTTP) and 443 (HTTPS) to have traffic routed to Coder.

How it works

In this example, we’ll make use of an nginx container to serve as the reverse proxy alongside the Coder container. We’ll also need to include our SSL certificate and key files in the nginx configuration file to enable TLS.

To get started, you'll need - A machine with Docker Engine and Docker Compose installed - A domain name - An SSL/TLS certificate

If you don’t have a certificate, our documentation covers how to procure one via LetsEncrypt.

Running an nginx container requires you to have an nginx.conf file on the host machine. To account for this, we’ll create one locally and define the server name, ports, and headers. See an example below:

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    default_type  application/octet-stream;
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

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

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }

        location / {
            proxy_pass   http://coder:7080;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }

    server {
        listen       443 ssl;
        server_name  <your-domain.com>;
        ssl_certificate      /letsencrypt/etc/letsencrypt/live/<your-domain.com>/cert.pem;
        ssl_certificate_key  /letsencrypt/etc/letsencrypt/live/<your-domain.com>/privkey.pem;
        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;
        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers  on;
        location / {
            proxy_pass   http://coder:7080;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }

    sendfile        on;
    keepalive_timeout  65;
    proxy_connect_timeout   90;
    proxy_send_timeout      90;
    proxy_read_timeout      90;
}

Once the server configuration is set, we can define our multi-container application with Docker Compose. All that’s needed is a single .yaml file. Example below:

version: ‘3’
services:
  nginx:
    container_name: nginx
    hostname: reverse
    image: nginx
    ports:
      - 80:80
      - 443:443
    volumes:
      - "/nginx:/etc/nginx"
      - "~/letsencrypt:/letsencrypt/"
  coder:
    hostname: coder
    image: codercom/coder:1.27
    container_name: coder
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ~/.coder:/var/run/coder
    ports:
      - 7080:7080
    environment:
      - DEVURL_HOST=*.your-domain.com

A few things to note: first, we’ve defined the ports key to publish ports 80 and 443 on the host network. Second, we’ve added the volumes key to create a bind mount volume. This will mount our local directories of /nginx and ~/letsencrypt into the nginx container. Both directories contain the nginx configuration and certificate files, respectively.

Now that we have our Docker Compose and nginx configuration files in place, we can start the containers! You’ll simply need to run docker-compose up -d to run both containers simultaneously. You should now be able to access your Coder instance from your domain.

For more detailed information on the steps above, see our documented guide.

Subscribe to our Newsletter

Want to stay up to date on all things Coder? Subscribe to our monthly newsletter and be the first to know when we release new things!