- Lower Kabete Rd, Nairobi 00100, Kenya
- Mon-Fri, 08:00am - 06:00pm
- +254 (798) 586113
- info@afriqsilicon.com
4 Min Read
Implementing CI/CD with GitHub Actions to Deploy Docker‑Compose Services
Step‑by‑step guide to set up a GitHub Actions pipeline that builds Docker images, pushes them to a registry and runs docker‑compose on a server, with tips
Step‑by‑step guide to set up a GitHub Actions pipeline that builds Docker images, pushes them to a registry and runs docker‑compose on a server, with tips
GitHub Actions workflow that builds and deploys a Docker‑Compose stack
1. Prerequisites
- A GitHub repository that contains your
docker-compose.prod.ymland aDockerfilefor each service. - A Docker registry where the images can be stored (DigitalOcean Container Registry, Docker Hub, etc.).
- SSH access to the target host (a DigitalOcean droplet, Hetzner server, etc.) with a private key added to the repository secrets.
- The host must have Docker Compose installed and a directory where the compose file will live.
2. Store secrets safely
Add the following secrets in Settings → Secrets → Actions:
SERVER_ENV_PROD, the full contents of the.envfile required by your compose file.DIGITALOCEAN_ACCESS_TOKEN, API token for the registry login.GL_SSH_HOST,GL_SSH_USERNAME,GL_SSH_SECRET,GL_SSH_PORT, SSH connection details.
To make the secrets visible inside the job you also need to declare the environment name on the job level, otherwise the secrets stay hidden see this answer.
3. Build and push images
The workflow below follows the pattern described in a high‑voted Stack Overflow answer see source. It checks out the code, creates the .env file, builds the images with Docker Compose, logs in to the registry and pushes the images.
name: Server Build & Push
on:
push:
branches: [main]
paths:
- 'server/**'
- 'shared/**'
- docker-compose.prod.yml
- Dockerfile
jobs:
build_and_push:
runs-on: ubuntu-latest
environment: production
steps:
- name: Checkout the repo
uses: actions/checkout@v2
- name: Create env file
run: |
touch .env
echo "${{ secrets.SERVER_ENV_PROD }}" > .env
cat .env
- name: Build image
run: docker compose -f docker-compose.prod.yml build
- name: Install doctl
uses: digitalocean/action-doctl@v2
with:
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
- name: Log in to DO Container Registry
run: doctl registry login --expiry-seconds 600
- name: Push image to DO Container Registry
run: docker compose -f docker-compose.prod.yml push
- name: Deploy Stack
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.GL_SSH_HOST }}
username: ${{ secrets.GL_SSH_USERNAME }}
key: ${{ secrets.GL_SSH_SECRET }}
port: ${{ secrets.GL_SSH_PORT }}
script: |
cd /srv/www/game
./init.sh
The ./init.sh script on the server is a tiny wrapper that runs the actual compose deployment:
docker stack deploy -c <(docker-compose -f docker-compose.yml config) game --with-registry-auth
The --with-registry-auth flag ensures the remote Docker engine can pull private images that were just pushed see source.
4. Deploy with docker‑compose on the target host
- Copy the compose file, the workflow pushes the images but does not copy the compose file; you can either keep the file in the repo and pull it on the server via
git pull, orscpit in the SSH step. - Run the init script, the SSH action executes
./init.sh, which expands the compose file with the.envvalues (Docker Compose does not read.envwhen used withdocker stack deploy, so the<(docker-compose … config)trick pre‑processes it). - Verify, after the stack is up, check the service status with
docker service lsanddocker stack ps game.
5. Common pitfalls and how to avoid them
- Missing environment on the job, without the
environment:key the secrets stay hidden and the build will fail when it tries to read$SERVER_ENV_PROD. - Registry login expiry, the
doctl registry login --expiry-seconds 600command gives a short‑lived token; keep the push step within that window. - Compose file paths, the
paths:filter in theon:block must include every file that, when changed, should trigger a new build; otherwise a change in a service’s Dockerfile will be ignored. - SSH host key verification, the Appleboy SSH action disables strict host key checking by default; for production you may want to add a known‑hosts file to avoid man‑in‑the‑middle risks.
- Bandwidth constraints, use multi‑stage Dockerfiles and keep base images small (Alpine, Distroless) to minimise the data transferred over limited African links.
Quick checklist you can copy
| Step | Done? |
|---|---|
| Add all required secrets in GitHub | |
Declare environment: production on the job | |
| Verify Dockerfiles are multi‑stage and small | |
| Ensure target host has Docker Compose and SSH key | |
Test the init.sh script manually before CI runs |
Deploying Docker‑Compose with GitHub Actions is a lightweight way to get continuous delivery without moving to a full Kubernetes stack. It fits well for NGOs, government agencies or startups that need a predictable, auditable pipeline while keeping operational costs low.
Sources
- Question 67023441, answer with 13 votes on Stack Overflow by Azarro, licensed CC BY-SA 4.0
- Problem with passing env variables from GitHub Actions to Docker container, accepted answer on Stack Overflow by Alex Lebedev, licensed CC BY-SA 4.0
Ready to turn this into a production‑grade pipeline? Talk to our team about it.
Frequently Asked Questions
Common questions on this topic, answered by the Afriq Silicon team.
What kind of Docker registry can I use with this workflow?
How are secret values passed to the workflow?
What if my compose file references environment variables?
Can I run this on a low‑bandwidth server in Kenya?
What happens if the deployment step fails?
Related Services
Working through this problem? These are the services we offer that connect to it.
System Orchestration
Make your infrastructure invisible, reliably fast, quietly resilient.
IT system orchestration and infrastructure from Afriq Silicon. We design scalable, secure, integrated IT environments for growing organizations.
Explore serviceProduct Design & Implementation
From idea to production-ready software, built right the first time.
Custom software product design and development from Afriq Silicon. We build scalable, user-centered applications, from concept to deployment.
Explore serviceTeam as a Service
Senior engineers embedded in your team, on your timeline.
Team as a Service from Afriq Silicon: embed senior engineers directly into your product team. Flexible, fast to onboard, no recruitment overhead.
Explore serviceRelated
Similar Articles
Stay Informed with Our Latest Articles: Explore the most recent insights, trends, and updates from our industry experts. Dive into a wealth of knowledge to keep you ahead in the ever-evolving tech landscape.
September 8th, 2026
The real reasons software procurement contracts stall in Kenya and how to fix them
Learn why many software procurement contracts Kenya fail, the clauses that cause risk, and how to embed compliance from day one for public‑sector projects.
June 18th, 2026
Software Development in Kenya: 2026 Buyer's Guide
A market briefing for procurement teams, program directors, and technology leaders considering Kenya as a software delivery base.
June 11th, 2026
Enterprise System Integration in Africa: A Practical Guide
What connecting business systems actually involves in African markets: M-Pesa callbacks, platforms with no API, and designing for the failure path.
June 2nd, 2026
Custom Software vs Off-the-Shelf in Kenya: 2026 Buyer's Guide
Off-the-shelf looks cheaper on paper. Here's the real Total Cost of Ownership comparison for Kenyan institutions in 2026.
Pages
- - Work
- - Services
- - Our Process
- - Contact Us
Solutions
- - Acts ML
- - Kilelehub
- - Other Projects
Legal
Contact
- - Lower Kabete Rd, Nairobi 00100, Kenya
- - Mon-Fri, 08:00am - 06:00pm
- - +254 (798) 586113
- - info@afriqsilicon.com