A complete CI/CD pipeline that automatically builds, tests, and publishes a Docker image to Docker Hub on every push β powered by GitHub Actions and Docker Compose.
| Tool | Purpose |
|---|---|
| Node.js + Express | Web application backend |
| Docker | Container runtime |
| Docker Compose | Multi-service local orchestration |
| GitHub Actions | CI/CD automation |
| Docker Hub | Container image registry |
| Nginx | Reverse proxy |
Developer pushes code
β
βΌ
βββββββββββββββββββββββββββββββββββ
β GitHub Actions Workflow β
β β
β Job 1: build-and-test β
β βββ Install dependencies β
β βββ Run unit tests β
β βββ Build Docker image β
β βββ Test Docker container β
β βββ Docker Compose integration β
β tests β
β β
β Job 2: build-and-push β
β (only on main branch) β
β βββ Login to Docker Hub β
β βββ Build & tag image β
β βββ Push to Docker Hub β
β βββ Verify pushed image β
βββββββββββββββββββββββββββββββββββ
β
βΌ
Docker Hub Registry
βββ saadcnx/docker-cicd-github-action:latest
βββ saadcnx/docker-cicd-github-action:main-49356c6
.
βββ .github/
β βββ workflows/
β βββ docker.yml # GitHub Actions CI/CD workflow
βββ scripts/
β βββ integration-test.sh # Integration test script
βββ app.js # Express web server
βββ test.js # Health check test
βββ package.json # Node.js dependencies
βββ Dockerfile # Container image definition
βββ docker-compose.yml # Local development stack
βββ docker-compose.test.yml # CI/CD integration test stack
βββ nginx.conf # Nginx reverse proxy config
- Docker
v20.10+ - Docker Compose
v2.x - Node.js
v18+ - A Docker Hub account
- A GitHub account
git clone https://github.com/saadcnx/docker-cicd-github-actions.git
cd docker-cicd-github-actions# Start the full stack
docker-compose up -d
# Run integration tests
./scripts/integration-test.sh
# Tear down
docker-compose downApp will be at http://localhost:3009 and via Nginx at http://localhost:8080
| Method | Endpoint | Description |
|---|---|---|
GET |
/ |
Returns app info and version |
GET |
/health |
Health check + uptime |
curl http://localhost:3009/
# {"message":"Hello from Docker CI/CD Demo!","timestamp":"...","version":"1.0.0"}
curl http://localhost:3009/health
# {"status":"healthy","uptime":42.3}Go to hub.docker.com β Create Repository β name it docker-cicd-github-action.
Docker Hub β Account Settings β Security β New Access Token
Use an access token instead of your password β it can be revoked independently.
In your GitHub repo β Settings β Secrets and variables β Actions:
| Secret Name | Value |
|---|---|
DOCKER_USERNAME |
Your Docker Hub username |
DOCKER_PASSWORD |
Your Docker Hub access token |
git add .
git commit -m "trigger: ci/cd pipeline"
git push origin mainWatch it run under the Actions tab in your GitHub repo.
- Installs Node.js dependencies
- Runs unit tests against live server
- Builds Docker image
- Spins up container and validates
/health - Runs full Docker Compose stack + integration tests
- Dumps service logs on failure (always)
- Cleans up all containers and volumes
- Requires
build-and-testto pass - Authenticates with Docker Hub
- Builds and pushes image with tags:
latestmain-<commit-sha>- branch name
- Pulls and smoke-tests the pushed image
| Tag | When created |
|---|---|
latest |
Every push to main |
main-<sha> |
Every push to main (commit-pinned) |
<branch-name> |
Any branch push |
Pull the latest image:
docker pull saadcnx/docker-cicd-github-action:latest
docker run -p 3009:3000 saadcnx/docker-cicd-github-actionnpm install
npm start &
npm testdocker-compose up -d
sleep 15
./scripts/integration-test.sh
docker-compose downThe integration script validates:
- App health endpoint responds correctly
- App returns expected JSON payload
- Nginx proxy forwards requests properly
- 10-request load test completes without errors
Docker Hub auth fails in pipeline
- Confirm secret names are exactly
DOCKER_USERNAMEandDOCKER_PASSWORD - Use an access token, not your account password
- Ensure the Docker Hub repo exists before the first push
Services don't start in CI
- Increase
sleepdurations in workflow steps - Check health check intervals in
docker-compose.yml - Review logs in the workflow's "Check service logs" step
Tests pass locally but fail in CI
- Services may need more startup time in CI environments
- Verify no port conflicts in the runner
- Check that all files referenced in
docker-compose.ymlare committed
Image build fails
- Run
docker build .locally to reproduce the error - Ensure
.dockerignoredoesn't exclude required files - Verify the base image tag is valid
- No secrets or credentials committed to the repository
- Docker Hub credentials stored exclusively as GitHub Secrets
- Container runs as a non-root user (
nodejsuid 1001) - Access tokens used instead of account passwords
- Images tagged with commit SHAs for full traceability
MIT β free to use, modify, and distribute.