Fediverse Logo FediMap - Join The Fediverse

How to use kamal‑proxy to expose your Docker containers.

How to use kamal‑proxy to expose your Docker containers.

Hello! It’s been a while since I last wrote a post for my blog, and this year I want to get back to documenting my technical findings. So here we are.

One of the last tasks I had at work was to set up our own SonarQube Community instance. We decided to set up our own instance because the plan was going to cost us $96 per month, while a server would cost us no more than $25.

Faced with that challenge, I looked for the easiest, most maintainable, and fastest way to do it and asked myself: why not deploy with Kamal but using a pre-built, public image, like SonarQube’s? The answer is that you can’t; Kamal expects to build and tag the image with the commit hash and then deploy and configure the proxy

From all of that, what I simply wanted was to get the benefits of the proxy, the TLS configuration, and automatic certificate renewal. That’s when I started researching kamal-proxy. Now I’ll show you how to quickly set up a Sonar container (it can be any service/application you want) and expose it using kamal-proxy.

The server

First of all, you need to have a server, configure your access via SSH, and install Docker. Once you have Docker installed and running, the first thing you’ll need to create is a network that is common to your service/application and the Kamal proxy. You can do this with the following command:

docker network create kamal-proxy-network

Depending on your settings, you may or may not need to use sudo to run Docker commands.

The application

Now, as I mentioned, I needed to launch SonarQube, but you may want to launch any other service. The important thing about this step is that you launch your application either with Docker Compose or simply with Docker and that the container uses the network we already created.

In my case, I used this docker-compose.yml:

services:
  sonarqube:
    image: sonarqube:community
    hostname: sonarqube
    container_name: sonarqube
    read_only: true
    depends_on:
      db:
        condition: service_healthy
    environment:
      SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar
      SONAR_JDBC_USERNAME: sonar
      SONAR_JDBC_PASSWORD: sonar
    volumes:
      - sonarqube_data:/opt/sonarqube/data
      - sonarqube_extensions:/opt/sonarqube/extensions
      - sonarqube_logs:/opt/sonarqube/logs
      - sonarqube_temp:/opt/sonarqube/temp
    ports:
      - "9000:9000"
    networks:
      - kamal-proxy-network
  db:
    image: postgres:17
    healthcheck:
      test: [ "CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}" ]
      interval: 10s
      timeout: 5s
      retries: 5
    hostname: postgresql
    container_name: postgresql
    environment:
      POSTGRES_USER: sonar
      POSTGRES_PASSWORD: sonar
      POSTGRES_DB: sonar
    volumes:
      - postgresql:/var/lib/postgresql
    networks:
      - kamal-proxy-network

volumes:
  sonarqube_data:
  sonarqube_temp:
  sonarqube_extensions:
  sonarqube_logs:
  postgresql:

networks:
  kamal-proxy-network:
    external: true

Check the network settings, where I use the same network created earlier.

The proxy

Now is when we use kamal-proxy. I couldn’t find any reference to how to “install” kamal-proxy, but I did find information on how to use it. That information is here on GitHub. This was the point where I got lost, which is why I wanted to write about it in this post.

I realized, in a deployment I have with Kamal, that kamal-proxy is simply another container, so I went to Docker Hub to look for an image and found it here. So, to launch a container with the proxy, I did it with the following command, always using the network we already created:

docker run -d --network kamal-proxy-network -p 80:80 -p 443:443 basecamp/kamal-proxy:latest

The output will give you the container’s hash ID. Copy it so you can enter the container:

docker exec -it 18cc77126e10b71b1ab1e0b88565868500f68af961a419c835b82e65a50cc4f7 bash

Now you will have the kamal-proxy command available and you can configure the deploys. In my case, to link the Sonar container and expose it through kamal‑proxy:

kamal-proxy deploy SonarQube --target sonarqube:9000
kamal-proxy@b6d74e5f5551:/$ kamal-proxy list
Service   Host  Path  Target          State    TLS  
service1  *     /     sonarqube:9000  running  no

For use tls:

kamal-proxy deploy SonarQube --target sonarqube:9000 --host sonar.example.com --tls

And with that, you’ll have your application running behind kamal-proxy. My main reasons for doing this were that kamal-proxy simplifies the entire configuration; as you saw, all you have to do is launch the container and run a couple of commands to get the proxy up and running. Plus, the best part is the automatic renewal of certificates.

I hope this has been helpful! If you were looking for an easy way to expose and configure a proxy for your containers, feel free to write to me with any questions or comments. Cheers!