DEV Community

Cover image for How to Build a CI/CD Pipeline Using GitHub Actions and Docker
Vector Skill Academy
Vector Skill Academy

Posted on

How to Build a CI/CD Pipeline Using GitHub Actions and Docker

A beginner-friendly look at how automation tests your code, builds your app, and gets it ready to ship

Why Do Developers Need CI/CD?

Imagine a developer who finishes a small feature on Friday afternoon. Before releasing it, they run the tests by hand, build the application, package it, and prepare the release. It takes twenty minutes. Next week, they do it again. And again.
Eventually something gets skipped: a test isn't run, a dependency is out of date on one laptop, or the release is built from the wrong version of the code. The bug reaches users, and nobody can say how.
Automation reduces this repetitive work and catches errors earlier, when they're cheaper to fix. That's the idea behind CI/CD, and here you'll see how GitHub Actions and Docker make it happen.

What Is a CI/CD Pipeline?

Continuous Integration (CI) means automatically checking and testing code changes whenever they're made. Instead of finding out a week later that two changes conflict, the team finds out within minutes.
Continuous Deployment (CD) means automating the release process. Depending on the team, this can stop at preparing a release artifact, or go all the way to deploying the application.
It helps to separate three things:
Testing code checks whether it works.
Preparing a release packages it into something you can ship.
Deploying puts that package into a running environment where users can reach it.
Here's a practical picture. A developer pushes a code change to GitHub. The pipeline starts automatically, runs the tests, and reports the result. If everything passes, the next stages run. If a test fails, the pipeline stops and the developer fixes the problem first.

What Roles Do GitHub Actions and Docker Play?

GitHub Actions automates tasks when something happens in your repository, such as a push or a pull request. Think of it as a very reliable assistant who watches your repository and follows a written checklist every time something changes.
Docker packages an application together with everything it needs to run, such as its runtime and libraries, into a container image. Think of a lunchbox: everything the meal needs is packed inside, so it tastes the same wherever you open it. That's what makes "it works on my machine" far less common.
A container registry stores images so other environments can pull them. If an image is the packed lunchbox, the registry is the shared fridge where finished lunchboxes wait until someone needs them.

How Does the Pipeline Work?

The workflow runs in a clear order:
A developer pushes code to GitHub.
GitHub Actions starts an automated workflow.
The workflow runs tests and checks.
Docker builds an image if the required checks succeed.
The image can be published to a container registry.
An optional deployment stage releases the application to a server or cloud environment.
flowchart LR
A[Push to GitHub] --> B[GitHub Actions starts]
B --> C[Run tests and checks]
C --> D{Checks passed?}
D -- No --> E[Stop and report failure]
D -- Yes --> F[Docker builds image]
F --> G[Publish to registry]
G -.-> H[Optional deployment]

Notice the dotted line to deployment. Publishing an image only stores it in a registry. Deployment is a separate step that actually runs the application somewhere.

Setting Up a Basic CI/CD Workflow

You don't need anything elaborate to start. The essential steps are:
Create a GitHub repository for your project.
Add your existing application to it, including its tests and a Dockerfile.
Create a workflow file inside the .github/workflows/ folder. Workflows are YAML files, and GitHub detects them automatically.
Choose when it runs. Commonly that's on pushes to your main branch and on pull requests.
Add steps that run your automated tests.
Build a Docker image after the checks succeed.
Here's a small example for a Python project:
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

permissions:
contents: read

jobs:
test-and-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install dependencies and run tests
run: |
pip install -r requirements.txt
pytest
- name: Build Docker image
run: docker build -t my-app:ci .

In plain English:
on is the trigger. This workflow runs when code is pushed to main, or when a pull request targets main.
permissions limits what the workflow is allowed to do. Here it can only read the repository, which is all this job needs.
runs-on picks the machine. GitHub provides a temporary Ubuntu runner that exists only for this run.
uses runs a ready-made action. The first checks out your code, and the second sets up Python.
run executes a command, such as installing dependencies and running tests.
Order matters. Steps run top to bottom, and if the tests fail, the job stops, so the Docker build never happens on broken code.
Action versions change over time, so check each action's page for the current major version. Notice what's missing: this workflow builds an image on a temporary runner but doesn't publish it, so the image disappears with the runner. Publishing needs a registry login and appropriate permissions, which you can add later.

How Docker Fits Into the Pipeline

A Dockerfile is a short text recipe describing how to build your image: which base image to start from, which files to copy in, which dependencies to install, and which command starts the app.
Container images make packaging more consistent because the recipe is the same every time. The pipeline builds the image on a clean machine, so it doesn't depend on whatever happens to be installed on a developer's laptop.
Conceptually, there are three stages. First you build the image from the Dockerfile. Then you test it, ideally by running it locally and checking that the app starts and responds. Finally you prepare it for distribution by tagging it clearly, for example with the commit it came from, and sending it to a registry.

Common Mistakes Beginners Should Avoid

Incorrect workflow configuration. YAML is strict about indentation, and a misplaced space can stop the workflow from running. Use an editor with YAML checking and read the error message in the Actions tab.
Tests failing before the build. That's the pipeline doing its job, not a bug. Read the test output, reproduce the failure on your own machine, and fix the code.
Missing application dependencies. If installation fails on the runner but works locally, a package is probably missing from your dependency file.
Incorrect Docker settings. Wrong file paths, the wrong port, or a bad start command are common. Build and run the image locally first to see the real error.
Exposing secrets in repository files. Never put passwords or tokens in code, workflow files, or Docker images. Store them in GitHub's secrets feature and reference them from the workflow.
Giving workflows unnecessary permissions. Start with read-only access and add more only where a job truly needs it.
Assuming a built image means a deployed application. Building an image, publishing it, and deploying it are three different things. Check which stages your pipeline actually includes.

How to Make a CI/CD Pipeline More Reliable

Once the basics work, these improvements add safety and speed:
Automated testing. The more meaningful your tests, the more the pipeline can protect you. This is the best place to invest first.
Secure secret management. Keep credentials out of files and logs. This becomes essential once you publish images or deploy.
Dependency updates. Outdated packages can carry known problems. Automated update tools can propose upgrades so they don't pile up.
Docker image scanning. Scanning checks images for known vulnerabilities before they ship, which matters once real users depend on them.
Build caching. Caching reuses unchanged work, so builds finish faster as the project grows.
Monitoring and rollback planning. Know how you'll notice a bad release and how you'd return to a previous good one. Tagging images by commit makes this much easier.
None of these is required on day one, and a successful build doesn't make an application secure or production-ready. Add improvements as your needs grow.
How CI/CD Skills Connect With Cloud and DevOps Careers
Automation, containers, cloud infrastructure, and deployment workflows sit at the center of modern software delivery. Developers who understand them can ship more reliably, and aspiring DevOps professionals build on exactly these foundations. The same pattern carries over to cloud deployment, where a published image might later run on a managed container service.
For people who want structured guidance in cloud and DevOps skills, an IT training institute in indore is one place to look for courses on deployment and automation. Learning platforms such as Vector Skill Academy can also be part of exploring structured study in cloud technologies and deployment practices, alongside hands-on projects.

Conclusion

GitHub Actions automates the steps of your workflow, and Docker packages your application consistently, so every change is tested and built the same way. A good CI/CD pipeline starts simple: automation that runs, tests you can trust, and configuration that keeps secrets safe.
From there, teams can gradually add image publishing and deployment as their needs grow. Whether you keep learning on your own or explore an IT training institute in indore for guided practice, building a small pipeline like this one is a great first project.
Which part of your development workflow would you like to automate first?

devops #docker #github #tutorial

Top comments (0)