We've already got Coolify running on an LXC and things are looking good. Now let's set up another LXC in Proxmox to act as our build server. Because why wouldn't we?
This is a continuation of this post about setting up Coolify on an LXC and using Tailscale SSH to connect everything
Build Server
Create a new LXC in Proxmox
General:
- Name: coolify-build
- Leave as "Unprivileged"
Template:
- Debian 12
Disks:
- 60GB
CPU:
- 4 Cores
Memory:
- 8GB (8192MiB)
Network:
- DHCP
DNS:
- Default
Before starting the LXC we need to log in to the root node and add the following to the config
nano /etc/pve/lxc/<lxc-number>.conf
Add these lines to the bottom
lxc.cgroup.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
Save and exit.
Start the new coolify-build
container
Update the system
apt update && apt upgrade
Install Curl
apt install curl
Tailscale
Log in to Tailscale and get a new script for setting up Tailscale on a linux server
Paste the script in the new Build LXC
Start Tailscale with SSH
tailscale up --ssh
If you followed the previous guide:
Go to the Machines section in the Tailscale dashboard and add the ACL Tag coolify
to the coolify-build node
Build LXC
Create a directory on the build server to hold all the registry data
cd ~ && mkdir registry-data && cd registry-data
Create a docker-compose.yml file in the registry directory
touch docker-compose.yml
Add the following to the docker-compose.yml
version: '3'
services:
registry:
image: registry:2
ports:
- "5000:5000"
volumes:
- ./registry-data:/var/lib/registry
- ./auth:/auth
environment:
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /var/lib/registry
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
restart: unless-stopped
Save the docker-compose.yml
file and create an auth
directory in the registry directory
mkdir auth
Still in the registry directory, generate auth credentials
docker run --entrypoint htpasswd httpd:2 -Bbn <registry-username> <registry-password> > auth/htpasswd
Change <registry-username>
and <registry-password>
to unique values. We'll use these to log in later.
Change the permissions for the newly created htpasswd
file
chmod 644 auth/htpasswd
Save the file and start the container. Whenever I run a new container for the first time I like to run it without the -d
flag just so I can see what's happening and then once everything is working how I want it to I just stop the container and re-run it with the flag. So start the container
docker compose up
Note: Docker will automatically try to use HTTPS for connecting to the registry, but since all of this is staying on the local network I'm not going to bother with that which means we have to make a change to the server's Docker config.
If you set up HTTPS you can skip the following step.
Still on the Build LXC:
Stop the Docker container
docker compose down
Open the daemon.json
file
nano /etc/docker/daemon.json
Add the following
{
"insecure-registries": ["<build-server-tailscale-ip>:5000"], // Add this line
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
...rest of the config
}
Save the file and restart Docker
systemctl restart docker
Open a terminal on the Coolify LXC and make sure you're able to connect to the build server
ping <build-server-tailscale-ip> -p 5000
If the ping fails, double check your Tailscale ACL file and/or the UFW rules on the Coolify LXC
Once you're able to connect, log in to the Docker registry
docker login <build-server-tailscale-ip>:5000
Enter the username/password you created earlier
Login Succeeded
Now SSH in to the build server and log in again
ssh root@<build-server-tailscale-ip>
docker login <build-server-tailscale-ip>:5000
You will probably need to edit the /etc/docker/daemon.json
file on the build server and the VPS. Just repeat the steps above for each server.
Note: I'm not sure why this file needs to be edited on all the servers, but when I remove it my builds fail 🤷
Make sure to run systemctl restart docker
after each change to daemon.json
Coolify LXC
Connect to the Coolify LXC and make sure you can SSH to the Build LXC
ssh root@<build-lxc-tailscale-ip>
If everything's good, go back in the Coolify dashboard and set up the build server
Coolify dashboard
- Click the Servers tab on the left menu
- Click the + Add button at the top
- Give the build server a name: Build LXC or whatever you want
- Put in the Tailnet IP Address for the Build LXC
- Keep the Private Key set to localhost's key
- Check the box that says Use it as a build server?
- Click Continue
Once you click the Verify button it should start to install a bunch of Docker stuff. Let that finish and we'll try to build something!
If you followed the last post and still have the blog template you can select that, otherwise add a new project.
Check the box that says Use a Build Server?
In the Docker Image textfield add
<build-server-tailscale-ip>:5000/<app-name>
Make sure your Build LXC docker compose is running if you stopped it earlier.
On the Build LXC
cd ~/registry-data
docker compose up -d
Save your settings, click the Deploy or Redeploy button at the top and watch your build-server CPU dance
If everything worked you should have a successful build and all the hard work was offloaded to another LXC. This lets us keep our Coolify LXC nice and lean.
Now let's set up our UFW rules on the Build LXC and call it a day
Build LXC
Install UFW
apt install ufw
Disable everything by default
ufw default deny incoming
ufw default deny outgoing
HTTPS and DNS are required to pull packages from the internet. This could probably be fine-tuned if you know exactly where all your packages are coming from, but for now this is fine.
Allow HTTPS
ufw allow in on 443
ufw allow out on 443
Allow out on HTTP
ufw allow out on 80/tcp
Allow DNS
ufw allow out on 53
Allow Tailscale
ufw allow in on tailscale0
ufw allow out on tailscale0
Stop and disable SSH
systemctl stop ssh
systemctl disable ssh
systemctl stop ssh.socket
systemctl disable ssh.socket
And there you have it! Coolify running locally on a Proxmox LXC with a local build server/Docker registry and everything communicates securley over an end-to-end encrypted tunnel thanks to Tailscale.