If you’re using Coolify to deploy Docker-based apps, you might notice that build times on your production server can get slow — especially when the server is handling both building and serving. In this post, I’ll show you how to move the Docker build step to GitHub Actions with GitHub Container Registry (GHCR), so Coolify only needs to pull and run a pre-built image.
With the default Coolify setup, every deployment means:
This took ~10 minutes because the server handled both building and serving the application.
Offload the build to GitHub Actions:
The Coolify deployment step now takes ~30 seconds.
Developer pushes to master
|
v
GitHub Actions (CI runner)
|
├── 1. Builds Docker image
├── 2. Pushes image to ghcr.io
└── 3. Triggers Coolify webhook
|
v
Coolify (production server)
|
├── Pulls pre-built image from ghcr.io
└── Restarts the container Go to your GitHub repository: Settings > Secrets and variables > Actions > Secrets tab > Repository secrets.
Add these two secrets:
| Secret Name | Description |
|---|---|
COOLIFY_WEBHOOK | The webhook URL from Coolify to trigger a deploy |
COOLIFY_TOKEN | The Coolify API token with deploy permission |
GITHUB_TOKENis automatically provided by GitHub Actions — no manual setup needed.
COOLIFY_TOKEN in GitHub secretsCOOLIFY_WEBHOOK in GitHub secretsSince the GitHub repository is private, the Coolify server needs to authenticate with GHCR to pull the image.
Create a GitHub Personal Access Token (Classic) with the read:packages scope, then SSH into your Coolify server:
sudo su -
echo "<YOUR_GITHUB_PAT>" | docker login ghcr.io -u <YOUR_GITHUB_USERNAME> --password-stdin You should see Login Succeeded.
Important: Run this as root. Coolify runs Docker as root, so credentials must be in
/root/.docker/config.json, not your regular user’s home directory.
Set Coolify to use the Docker Compose build pack. The docker-compose.yml references the pre-built GHCR image directly (no build: directive), so Coolify will pull instead of build.
Create .github/workflows/deploy.yml:
name: Build and Deploy
on:
push:
branches:
- master
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Trigger Coolify deploy
if: success()
run: |
curl -X GET "${{ secrets.COOLIFY_WEBHOOK }}"
-H "Authorization: Bearer ${{ secrets.COOLIFY_TOKEN }}"
--fail --silent --show-error Key things to note:
latest and the commit SHA — this enables easy rollbackstype=gha cache means subsequent builds only rebuild changed layersYour docker-compose.yml should reference the GHCR image instead of building locally:
services:
wordpress:
image: ghcr.io/your-org/your-repo:latest
networks:
- coolify
volumes:
- ./uploads:/var/www/vhosts/localhost/html/wp-content/uploads
networks:
coolify:
external: true Just push to master:
git push origin master Monitor the build in the Actions tab of your GitHub repository.
Docker on the Coolify server isn’t authenticated with GHCR, or the login was done as a non-root user. Fix by running docker login as root on the server.
The repository may not have the correct GITHUB_TOKEN permissions. Go to Settings > Actions > General > Workflow permissions and set it to Read and write permissions.
Verify both COOLIFY_WEBHOOK and COOLIFY_TOKEN secrets are correct. Check that the API token has deploy permission.
Each build is tagged with the commit SHA. To roll back, update the image tag in docker-compose.yml:
image: ghcr.io/your-org/your-repo:<commit-sha> Then trigger a redeploy in Coolify or push the change.
Moving the Docker build from Coolify to GitHub Actions with GHCR is a simple but impactful optimization. Your production server only pulls and runs pre-built images, reducing deployment time significantly and freeing up server resources for actually serving your application.