CI/CD Pipeline with Docker
Containerize a Python app and automate builds with GitHub Actions.
Introduction
30 Second Summary
Every time you update an app on your phone, someone had to package that code, test it, and ship it without breaking anything. What if that entire process ran automatically every time you saved your work?
In this project, you will build a Python web app, package it into a Docker container, and create a GitHub Actions pipeline that automatically builds and verifies your app every time you push code.
What You'll Build
A simple Python Flask web app containerized with Docker and automated with a GitHub Actions CI/CD pipeline.
By the end of this project, you'll have:
- A working Docker container running a Python web app on your Mac.
- A GitHub Actions workflow that automatically builds and tests your Docker image on every push.
- A public GitHub repository showcasing your first DevOps project with a green checkmark.
- Secret Mission: Add automated tests that gate your pipeline, ensuring broken code never gets built into an image.
Are there any prerequisites?
None. This project includes full setup instructions for every tool you need. You just need a Mac with an internet connection and a free GitHub account.
Install Docker Desktop and Set Up Your Repository
Every DevOps pipeline needs two foundations: a way to package your application into a portable unit, and a place to automate the build process. Docker handles packaging, and GitHub Actions handles automation.
In this step, you will install Docker Desktop on your Mac and create a GitHub repository that will house your pipeline. This gives you the tools and workspace to build everything in the steps ahead.
In this step, get ready to:
- Install Docker Desktop and verify it runs on your Mac.
- Create and clone a public GitHub repository.
- Set up the folder structure GitHub Actions needs.
Download and install Docker Desktop
Docker Desktop is the application that lets you build and run containers on your Mac. It installs everything you need, including the docker command line tool.
- Open the Docker Desktop download page in your browser:
- https://docs.docker.com/desktop/setup/install/mac-install/
- Click the download button that matches your Mac (Apple chip or Intel chip).
- Double-click the downloaded Docker.dmg file to open the installer.
- Drag the Docker icon into the Applications folder.
What happens during the first launch?
The first time Docker Desktop opens, macOS runs security checks that can take a minute or two. You will also see a license agreement and a settings screen. The steps below walk you through both.
- Press Cmd+Space to open Spotlight.
- Type Docker and press Enter to launch Docker Desktop.
- Accept the Docker Subscription Service Agreement when it appears.
- Select Use recommended settings and click Finish.
- Wait for the Docker engine to start. You will see a whale icon in your menu bar when it is ready.
What is Docker?
Docker packages your application and all its dependencies into a single unit called a container. Containers run the same way on any machine, which eliminates "it works on my machine" problems.
Docker Desktop is the macOS app that provides the Docker engine, CLI tools, and a graphical dashboard for managing containers. It is free for personal use and education.
Now verify that Docker installed correctly.
- Press Cmd+Space to open Spotlight.
- Type Terminal and press Enter to open it.
- Check your Docker installation by running this command:
docker --version
✔️ I see a version number
You should see output like Docker version 27.x.x, build ... printed in your terminal. Docker is installed and ready to go.
ⓧ Command not found
If Terminal says the docker command is not found, Docker Desktop has not finished setting up its CLI tools.
- Make sure Docker Desktop is running. Look for the whale icon in your menu bar.
- Close Terminal completely, then reopen it. This refreshes your PATH so the new docker symlink is picked up.
- If it still does not work, open Docker Desktop, go to Settings then Advanced, and confirm CLI tools are installed to /usr/local/bin.
After making changes, close and reopen Terminal, then run docker --version again.
Create and clone your GitHub repository
Your pipeline needs a home on GitHub. You will create a public repository so that GitHub Actions runs for free with unlimited minutes.
- Open github.com in your browser and sign in.
- Click the + icon in the top right corner.
- Select New repository.
- Enter my-first-pipeline as the repository name.
- Set the visibility to Public.
- Leave all other settings at their defaults.
- Click Create repository.
Why a public repository?
GitHub Actions is completely free and unlimited for public repositories. Private repositories have a monthly minutes cap on the free plan, but public repos have no limit.
Now clone the repository to your Mac so you can work on it locally.
- In Terminal, move to your Desktop by running this command:
cd ~/Desktop
- Clone the repository by running this command:
git clone https://github.com/[[YOUR_USERNAME="your GitHub username"]]/my-first-pipeline.git
- Move into the repository folder by running this command:
cd my-first-pipeline
- Confirm you are inside the repository by running:
pwd
You should see a path ending in my-first-pipeline, something like /Users/yourname/Desktop/my-first-pipeline.
Clone failing with permission errors?
If git clone asks for credentials or fails, make sure you are signed into GitHub in your browser and that the repository name matches exactly. You may need to set up a personal access token or SSH key if this is your first time using git on this Mac.
help me set up Git authentication on my Mac so I can clone my repository
Create the folder structure for GitHub Actions
GitHub Actions looks for workflow files inside a specific directory in your repository. You need to create this folder before you can add a pipeline later.
- Create the workflows directory by running this command:
mkdir -p .github/workflows
What does this command do?
The mkdir -p command creates a directory and any parent directories that do not exist yet. Here it creates a .github folder with a workflows folder inside it.
GitHub Actions automatically runs any .yml files it finds in .github/workflows/. This is where your CI/CD pipeline definition will live.
- Confirm the directory was created by running:
ls .github
You should see workflows listed in the output.
Don't see the workflows folder?
Make sure you are inside the my-first-pipeline directory. Run pwd to check your current location. If you are somewhere else, run cd ~/Desktop/my-first-pipeline first and try the mkdir command again.
help me find or create the .github/workflows directory in my repository
Your environment is ready. Docker is installed, your repository is cloned, and the GitHub Actions folder structure is in place. Next up, you will write the Flask application that your pipeline will build and test.
Create Your Flask Application
Your environment is set up and your repository is ready to go. Now you need an application to containerize.
In this step, you'll create a Python web app using Flask, a lightweight web framework. Your app will have two routes: a welcome page and a health check endpoint that returns JSON data.
In this step, get ready to:
- Create a Flask web server with two routes.
- Pin your Flask dependency in a requirements file.
- Test your app locally to confirm it responds correctly.
Create your Flask web server
Flask lets you define URL routes that return responses to visitors. You'll start with a welcome route and then add a health check that your pipeline will use later to verify the app is working.
- Create a new file called app.py in your my-first-pipeline folder by running:
touch app.py
- Confirm the file exists by running:
ls
You should see app.py and the .github folder in the output.
- Open app.py in your text editor. If you don't have a code editor installed, press Cmd+Space, type TextEdit, and press Enter to open TextEdit.
- If using TextEdit, click Format in the menu bar, then select Make Plain Text before pasting any code.
- Add the following code to app.py:
from flask import Flask, jsonify
# Create the Flask application instance
app = Flask(__name__)
# Root route - displays a welcome message
@app.route("/")
def home():
return "Welcome to my first CI/CD pipeline!"
What does this code do?
- from flask import Flask, jsonify imports the Flask class and a helper for returning JSON responses.
- app = Flask(__name__) creates your application instance. Flask uses __name__ to know where to find your project files.
- @app.route("/") registers the home() function to handle requests to the root URL.
- Below the home() function, add the health check route and the code that starts the server:
# Health check route - returns JSON status for monitoring
@app.route("/health")
def health():
return jsonify({"status": "healthy"})
# Start the server when this file is run directly
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
What does this code do?
- The /health route returns a JSON response with a status field. Your CI/CD pipeline will hit this endpoint later to verify the app is alive.
- jsonify() converts a Python dictionary into a proper JSON response with the correct headers.
- host="0.0.0.0" tells Flask to accept connections from any network interface. This is required when running inside a Docker container.
- The if __name__ == "__main__" block ensures the server only starts when you run the file directly, not when it's imported by another module.
- Save app.py.
✔️ Awesome, I've got everything!
Great. Make sure you've saved the file before moving on.
ⓧ I'd like to double check the full code
Here's the complete app.py file for comparison:
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/")
def home():
return "Welcome to my first CI/CD pipeline!"
@app.route("/health")
def health():
return jsonify({"status": "healthy"})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
Pin your Flask dependency
Python projects use a requirements.txt file to list the exact packages the project needs. Pinning the version number ensures everyone working on the project uses the same version of Flask.
- Create a requirements.txt file by running:
echo "flask==3.1.3" > requirements.txt
What does this command do?
The echo command writes the text flask==3.1.3 into a new file called requirements.txt. The == pins Flask to version 3.1.3 so your project always uses the same version.
- Confirm the file was created by running:
cat requirements.txt
You should see flask==3.1.3 printed in the terminal.
Don't see the file content?
Make sure you ran the echo command from inside your my-first-pipeline directory. Run pwd to check your current location.
help me figure out why my requirements.txt file wasn't created
Test your app locally
Before you containerize anything, it's good practice to make sure the app runs correctly on your machine first. This way you'll know any problems later are caused by Docker configuration, not your application code.
- Install Flask by running:
pip install -r requirements.txt
Seeing 'command not found' for pip?
On macOS, you may need to use pip3 instead of pip. Try running pip3 install -r requirements.txt instead.
help me install Flask on my Mac
You should see output showing Flask and its dependencies being downloaded and installed.
- Start the Flask server by running:
python app.py
You should see output in your terminal indicating the server is running on port 5000.
Seeing 'command not found' for python?
On macOS, try python3 app.py instead. macOS often requires python3 for Python 3 commands.
help me run my Flask app on macOS
- Open your web browser.
- Type http://localhost:5000/health into the address bar and press Enter.
You should see {"status":"healthy"} displayed in the browser.
What is a health check endpoint?
A health check endpoint is a lightweight URL that monitoring tools and pipelines use to confirm an application is alive and responding. Yours returns a simple JSON status that your CI/CD pipeline will check automatically every time it builds.
- Go back to your terminal and press Ctrl+C to stop the Flask server.
Browser shows 'connection refused'?
Make sure the Flask server is still running in your terminal. You should see the server output without returning to the command prompt.
Also check that you're visiting http:// and not https:// in your browser address bar.
help me connect to my Flask app in the browser
Your Flask app is running and responding to requests. Next up, you'll package this app into a Docker container so it can run identically on any machine.
Containerize Your App with Docker
Your Flask app works on your machine. But what happens when you want to run it on a server, a teammate's laptop, or inside a CI/CD pipeline?
Docker solves this by packaging your app and all its dependencies into a container that runs identically everywhere. In this step, you'll write a Dockerfile that tells Docker exactly how to build your app into a portable container image.
In this step, get ready to:
- Create a Dockerfile that packages your Flask app.
- Build a Docker image and run it as a container.
- Verify your containerized app responds correctly.
Create your Dockerfile
A Dockerfile is a recipe that tells Docker how to build your app into an image. Each line is an instruction: which base image to start from, which files to copy in, and which command to run when the container starts.
- Create a new file called Dockerfile in your my-first-pipeline folder (the same folder where app.py lives).
- Add the following content to your Dockerfile:
# Use a lightweight Python image as the starting point
FROM python:3.13-slim
# Set the working directory inside the container
WORKDIR /app
# Copy the requirements file and install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy the application code into the container
COPY app.py .
# Tell Docker this container listens on port 5000
EXPOSE 5000
# Run the Flask app when the container starts
CMD ["python", "app.py"]
What does this Dockerfile do?
- FROM python:3.13-slim starts from a minimal Python image so you don't have to install Python manually.
- WORKDIR /app creates and switches to a folder called /app inside the container.
- COPY requirements.txt . and RUN pip install install your Flask dependency before copying the app code. This order lets Docker cache the install step if your dependencies haven't changed.
- COPY app.py . brings your application code into the container.
- CMD ["python", "app.py"] tells Docker what command to run when the container starts.
- Save Dockerfile.
- Confirm the file exists by running:
ls
You should see Dockerfile listed alongside app.py and requirements.txt.
Don't see the Dockerfile?
Make sure the file is named exactly Dockerfile with a capital D and no file extension. It should not be Dockerfile.txt or dockerfile.
Also confirm you saved it inside your my-first-pipeline folder, not a subfolder.
Still stuck? Why isn't Docker finding my Dockerfile?
✔️ Awesome, I've got everything!
Great. Make sure you've saved Dockerfile before moving on.
ⓧ I'd like to double check the full code
Here is what your Dockerfile should look like:
FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 5000
CMD ["python", "app.py"]
Build and run the Docker container
Now that your Dockerfile is ready, you can build a Docker image from it. The image is a snapshot of your app and its environment. Once built, you can run it as a container.
- Build your Docker image by running this command:
docker build -t my-first-pipeline .
What does this command do?
- docker build reads your Dockerfile and executes each instruction to create an image.
- -t my-first-pipeline tags (names) the image so you can refer to it later.
- . tells Docker to use the current directory as the build context (where to find your Dockerfile and app files).
You should see Docker downloading the Python base image and running each step from your Dockerfile. The build is complete when you see output confirming all steps finished successfully.
Build failing or Docker not found?
Make sure Docker Desktop is running. You should see the Docker whale icon in your menu bar. If the docker command is not found, try quitting and reopening your Terminal after Docker Desktop finishes starting.
If you see an error about the Dockerfile not being found, make sure your Terminal is inside the my-first-pipeline folder where your Dockerfile lives.
Need more help? Why is my Docker build failing?
- Run your new image as a container by running this command:
docker run -d -p 5000:5000 my-first-pipeline
What do these flags mean?
- -d runs the container in detached mode (in the background) so it does not lock up your terminal.
- -p 5000:5000 maps port 5000 on your Mac to port 5000 inside the container. This lets you access the app through your browser.
- my-first-pipeline is the name of the image you just built.
Docker prints a long string of characters. This is your container ID, confirming the container started successfully in the background.
Seeing a port conflict error?
If you see an error mentioning port 5000 is already in use, the Flask app from the previous step might still be running. Switch to the terminal where you ran python app.py earlier and press Ctrl+C to stop it. Then try the docker run command again.
Still stuck? Help me fix my Docker port conflict
Test the containerized app
Your app is now running inside a Docker container. Let's verify it responds just like it did when you ran it directly on your Mac.
- Open your browser and visit http://localhost:5000/health.
You should see the same JSON response as before: {"status": "healthy"}. This time, the response is coming from inside your Docker container.
Why does this matter?
You just proved that your app runs identically inside a container. Anyone with Docker installed can run this same image and get the exact same result, regardless of their operating system or installed software.
This is what makes containers so powerful for CI/CD. Your pipeline can build and test this image in the cloud, confident it behaves the same way it does on your machine.
- Stop the running container by running this command:
docker stop $(docker ps -q --filter ancestor=my-first-pipeline)
What does this command do?
The inner command docker ps -q --filter ancestor=my-first-pipeline finds the container ID of any running container based on your image. The outer docker stop command then stops that container.
Docker prints the container ID again, confirming it has been stopped.
Your app is now fully containerized. Next up, you'll create a GitHub Actions pipeline that builds this Docker image automatically every time you push code.
Create Your GitHub Actions CI/CD Pipeline
Your Docker container runs perfectly on your Mac, but right now you have to build and run it manually every time you make a change. The real power of DevOps comes from automation.
GitHub Actions is a CI/CD service built directly into GitHub. You will create a workflow file that tells GitHub to automatically build your Docker image and verify your app every time you push code.
In this step, get ready to:
- Create a GitHub Actions workflow file that builds your Docker image.
- Add a verification step that tests the containerized app.
- Push your code and trigger the pipeline on GitHub.
Create the workflow file
GitHub Actions workflows live inside a special directory in your repository. The .github/workflows folder you created earlier is exactly where GitHub looks for workflow instructions.
- Create a new file called ci.yml inside your .github/workflows directory by running this command:
touch .github/workflows/ci.yml
- Open ci.yml in your text editor and paste the following code:
name: CI/CD Pipeline
on:
push:
branches:
- main
jobs:
build-and-verify:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v7
- name: Build Docker image
run: docker build -t my-first-pipeline .
- name: Run container
run: docker run -d -p 5000:5000 --name test-container my-first-pipeline
What does this code do?
- name: CI/CD Pipeline gives your workflow a human-readable name that shows up in the GitHub Actions tab.
- on: push: branches: - main tells GitHub to run this workflow every time you push code to the main branch.
- runs-on: ubuntu-latest spins up a fresh Linux virtual machine in GitHub's cloud to run your pipeline steps.
- actions/checkout@v7 copies your repository code into the virtual machine so the pipeline can access your files.
- The Build and Run steps use the same docker build and docker run commands you ran locally in the previous step.
What is YAML?
YAML is a configuration language that uses indentation (spaces) to define structure. It is the standard format for GitHub Actions workflows. Indentation matters in YAML, so make sure your spaces line up exactly as shown.
- Save ci.yml.
- Confirm the file exists by running:
ls .github/workflows/
You should see ci.yml in the output.
Don't see ci.yml?
- Make sure you ran the touch command from inside your my-first-pipeline repository folder.
- Check that the .github/workflows directory exists by running ls -la .github/workflows/.
- Help me figure out what went wrong
Add the health check verification
Your pipeline builds and runs the container, but it does not yet check whether the app is actually working. A real CI/CD pipeline should verify that the container started successfully.
- In ci.yml, add the following steps directly below the Run container step:
- name: Wait for container to start
run: sleep 3
- name: Verify health endpoint
run: |
response=$(curl -s http://localhost:5000/health)
echo "Response: $response"
echo "$response" | grep -q '"status"' || (echo "Health check failed!" && exit 1)
What does this code do?
- sleep 3 waits 3 seconds for the Flask app to finish starting inside the container. Without this pause, the next step might run before the app is ready.
- curl -s sends a silent HTTP request to the health endpoint running inside the container.
- grep -q '"status"' checks that the response contains the word "status". If it does not, the pipeline fails with an error message and exit 1 tells GitHub the step failed.
- Save ci.yml.
You will not see a visible change yet. The next section pushes the code to GitHub where the pipeline runs in the cloud.
Seeing indentation issues in your YAML?
- YAML is strict about spaces. Make sure the new steps line up with the existing steps above them (6 spaces before each -).
- Do not use tabs. YAML requires spaces only.
- Help me check my YAML indentation
✔️ Awesome, I've got everything!
Great. Double check you have saved ci.yml before moving on.
ⓧ I'd like to double check the full code
Here is the complete ci.yml file for comparison:
name: CI/CD Pipeline
on:
push:
branches:
- main
jobs:
build-and-verify:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v7
- name: Build Docker image
run: docker build -t my-first-pipeline .
- name: Run container
run: docker run -d -p 5000:5000 --name test-container my-first-pipeline
- name: Wait for container to start
run: sleep 3
- name: Verify health endpoint
run: |
response=$(curl -s http://localhost:5000/health)
echo "Response: $response"
echo "$response" | grep -q '"status"' || (echo "Health check failed!" && exit 1)
Push your code to GitHub
Your workflow file is ready. Now you need to commit all your project files and push them to GitHub so the pipeline can trigger.
- Commit and push all your files by running these commands:
git add .
git commit -m "Add CI/CD pipeline with Docker"
git push origin main
What do these commands do?
- git add . stages all files in the current directory (your app, Dockerfile, and workflow).
- git commit -m "..." saves a snapshot of your changes with a description.
- git push origin main uploads your code to GitHub, which triggers the workflow you just created.
GitHub may ask you to authenticate
If this is your first push, Git may prompt you for your GitHub username and password. GitHub no longer accepts account passwords for Git operations. You need to enter a Personal Access Token (PAT) in the password field instead.
To create a PAT, go to GitHub.com, click your profile icon, then Settings, then Developer settings, then Personal access tokens. Generate a token with the repo scope and paste it when prompted for a password.
- Open your repository on GitHub in your browser.
- Click the Actions tab at the top of the page.
You should see your workflow running (or already completed with a green checkmark). The workflow name CI/CD Pipeline appears in the list.
Don't see your workflow in the Actions tab?
- Make sure your push succeeded. Check your terminal for any errors from git push.
- Verify that ci.yml is at the correct path: .github/workflows/ci.yml. GitHub only detects workflows in this exact location.
- Confirm your repository has a main branch (not master). Your workflow triggers on pushes to main only.
- Help me figure out why my pipeline isn't triggering
Your pipeline is live. Next up, you will make a code change and watch the entire automation loop trigger again to prove it works on every push.
Watch Your Pipeline in Action
Your pipeline ran successfully in the last step. You now have a working CI/CD loop that triggers every time code is pushed to the main branch.
In this step, you will explore the pipeline logs in real time and prove that the automation works by pushing a code change and watching it trigger a brand-new pipeline run.
In this step, get ready to:
- Explore the completed pipeline run and review each step's logs.
- Push a code change to trigger a second pipeline run automatically.
- Confirm two green checkmarks in the Actions tab.
Explore your completed pipeline run
Every workflow run generates a detailed log of each step. Reviewing these logs helps you understand exactly what the pipeline does each time it runs.
- Open your my-first-pipeline repository on GitHub.
- Click the Actions tab in the top navigation bar.
- In the left sidebar, click CI/CD Pipeline (this is the workflow name from your ci.yml file).
- Click the most recent workflow run name to open the workflow run summary.
- Under Jobs or in the visualization graph, click the build-and-verify job.
- Click each step to expand its logs and see what happened.
What should I see in the logs?
Each step from your ci.yml file appears as an expandable section. You will also see two extra steps that GitHub adds automatically: Set up job and Complete job.
- Check out code shows the repository being cloned into the runner.
- Build Docker image shows each layer of your Dockerfile being built (just like your local docker build output).
- Run container starts your app inside a container on the runner.
- Verify health endpoint prints the JSON response from your /health route, confirming the app is alive.
Push a code change to trigger a new run
The whole point of CI/CD is that the pipeline triggers itself whenever code changes. Let's prove that by making a small edit and pushing it.
- Switch back to your terminal (still inside the my-first-pipeline directory).
- Open app.py in your text editor.
- Find this line:
return "Welcome to my first CI/CD pipeline!"
- Change the text inside the quotes to something different, for example Welcome to my updated CI/CD pipeline!.
- Save app.py.
- Commit and push the change by running these commands:
git add .
git commit -m "Update welcome message"
git push origin main
What happens after you push?
The moment your push reaches GitHub, the on: push trigger in ci.yml fires. GitHub spins up a fresh ubuntu-latest runner and starts executing the workflow steps from scratch.
This means every push gets a clean environment. Nothing is left over from the previous run.
- Switch back to your browser and return to the Actions tab on GitHub.
- You should see a new workflow run appear within a few seconds (with an orange dot indicating it is in progress).
- Click on the new run to watch each step execute in real time.
Don't see a new run?
Refresh the Actions page. If no run appears after 30 seconds, confirm your push succeeded by checking the commit history on your repository's main page.
If the push didn't go through, make sure you are on the main branch locally. The workflow only triggers on pushes to main.
Help me figure out why my GitHub Actions workflow didn't trigger.
Confirm two green checkmarks in the Actions tab
Once the second run finishes, you have definitive proof that your CI/CD loop works end to end.
- Wait for the new run to finish (it typically takes under a minute).
- Return to the main Actions tab.
- You should now see two workflow runs listed, each with a green checkmark icon.
What do the green checkmarks mean?
A green checkmark means every step in the workflow completed without errors. Your code was checked out, your Docker image built successfully, the container started, and the health endpoint responded with the expected JSON.
If any step had failed, you would see a red X icon instead. GitHub automatically expands failed steps so you can diagnose the problem immediately.
Seeing a red X instead?
If your second run failed, click the run and expand the failed step. The most common cause is a syntax error in app.py from your edit (for example, a missing quote or parenthesis).
Fix the error locally, commit, and push again. The pipeline will trigger a fresh run.
Help me debug my failed GitHub Actions workflow run.
Secret mission
Add Automated Tests to Your Pipeline
Your pipeline builds and runs your app, but what if broken code slips through? Real CI/CD pipelines include automated tests that catch bugs before they reach production. In this mission, you'll add a test suite that gates your pipeline so broken code never gets built into an image.
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally and uses GitHub Actions on a public repository, so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- Local project files (the my-first-pipeline folder on your Desktop).
- Docker images and containers built locally.
- Docker Desktop (installed application).
- GitHub repository (my-first-pipeline).
Keep everything running
No action needed. Choose this if you are still actively building or plan to extend this project soon.
- Docker Desktop continues running in the background. You can quit it from the menu bar icon if you want to free up memory.
- Your GitHub repository stays public with your green-checkmark pipeline visible.
- Your local project files remain on your Desktop.
Pause - I'll come back to this later
Shut down running processes to free up memory, but keep your files and repository intact.
- Stop any running Docker containers by running:
docker stop $(docker ps -q)
- Quit Docker Desktop by clicking the Docker icon in your menu bar and selecting Quit Docker Desktop.
- Your GitHub repository and local files stay as they are.
When you come back, reopen Docker Desktop and your environment is ready to go.
Delete - I don't want to use this again
Remove all project resources and start fresh.
- Remove Docker images, stopped containers, and build cache by running:
docker system prune -a
What does this command do?
This removes all stopped containers, unused networks, dangling images, and build cache from your local Docker environment.
- Delete your local project folder by running:
cd ~/Desktop
rm -rf my-first-pipeline
- Delete your GitHub repository:
- Open your my-first-pipeline repository on GitHub.
- Click Settings under the repository name.
- Scroll down to the Danger Zone section and click Delete this repository.
- Click I want to delete this repository.
- Click I have read and understand these effects.
- Type the repository name to confirm, then click Delete this repository.
- Uninstall Docker Desktop:
- Open Docker Desktop.
- Click the Troubleshoot icon in the top-right corner of the Docker Desktop Dashboard.
- Select Uninstall and confirm by selecting Uninstall again.
- Move the Docker application to the Trash.
Nice Work!
Nice Work!
You just built a fully automated CI/CD pipeline from scratch. Every time you push code to GitHub, your pipeline checks out the code, builds a Docker image, and verifies your app is running correctly.
You've learned how to:
- Containerize a Python Flask application using a Dockerfile that packages your app into a portable, reproducible image.
- Create a GitHub Actions CI/CD pipeline that automatically builds and verifies your Docker image on every push to the main branch.
- Run the full automation loop end to end: push code, pipeline triggers, container builds, and app verified with a green checkmark.
- Secret Mission: Added automated tests that gate the pipeline, ensuring broken code never gets built into an image.
Ready to quiz yourself?