Andrés
•
2 January 2026
•
4 mins
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.
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.
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.
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!
Like it? Share it!