- Lower Kabete Rd, Nairobi 00100, Kenya
- Mon-Fri, 08:00am - 06:00pm
- +254 (798) 586113 +254 (728) 922269
- 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.
October 1st, 2026
Outsourcing vs Local Delivery: risk analysis for African public‑sector IT
Find a concise risk‑focused comparison of outsourcing, local vendors and in‑house teams for government software projects, with checklists and next steps.
September 25th, 2026
Running Mission‑Critical Systems in Low‑Connectivity, Unreliable Power
Learn how to design, build and operate resilient enterprise software in Africa where network and electricity are spotty. Get practical steps, trade‑offs and
September 22nd, 2026
Implementing ERP in African Public Sector: procurement, migration, and sustainability
Learn how to navigate public‑sector ERP procurement cycles, migrate legacy data, and build a sustainable system that stays operational for the long term.
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.
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
- - +254 (728) 922269
- - info@afriqsilicon.com