If you’ve ever dipped your toes into DevOps, chances are you’ve heard about Docker, Jenkins, or automation at some point. In this post, I’ll walk you through a fun little project where I used Docker Compose and Python to automate the creation of a Jenkins environment from scratch. Whether you're just getting into CI/CD tools or looking to add more automation into your workflow, this post should give you some hands-on insight.
🐳 What is Docker and Why It’s a Game-Changer
Docker is basically your best friend when it comes to creating consistent environments across machines. It lets you package applications and all their dependencies into containers so they can run anywhere — your laptop, a server, a cloud instance — without the "but it works on my machine" headache.
It’s lightweight, fast, and makes spinning up complex environments surprisingly easy. Instead of installing Jenkins (or any other tool) directly on your machine, you just run a container with everything preconfigured. Simple, clean, and reversible.
📦 Enter Docker Compose
Docker Compose is like Docker’s orchestration tool for your local environment. It allows you to define multi-container applications in a single docker-compose.yml
file and spin them all up with just one command. Perfect for projects that need more than one moving piece (like Jenkins and a custom volume or service).
It’s kind of like having a remote control for all your Docker containers — and trust me, once you start using it, you’ll wonder how you ever managed without it.
⚙️ Why Automation Matters
Manually repeating setup steps is a productivity killer. Automation helps reduce errors, saves time, and makes your projects way more maintainable. For this project, I automated:
- Building a custom Jenkins image
- Launching a Jenkins container
- Mounting persistent data
- Verifying that the setup worked
Using Python, I wrapped all those commands into a simple script so that I (or anyone else) could spin up the environment with one command. Clean and scalable.
🛠️ Step-by-Step: Building the Project
Let’s break down what I actually did to bring this to life.
1. Wrote the docker-compose.yml
This file defines the Jenkins container, ports, volume, and image. Here’s a simplified version:
version: "3.8"
services:
jenkins:
build: .
container_name: my-custom-jenkins
ports:
- "8080:8080"
volumes:
- jenkins_data_custom:/var/jenkins_home
volumes:
jenkins_data_custom:
This lets me build a Jenkins container from a custom image and persist the Jenkins home directory with a volume.
2. Created a Dockerfile
The Dockerfile defines the custom Jenkins image:
FROM jenkins/jenkins:lts
USER root
RUN apt-get update && apt-get install -y \
sudo \
curl \
git \
&& rm -rf /var/lib/apt/lists/*
USER jenkins
Pretty straightforward. It gives Jenkins root privileges temporarily to install any required packages.
3. Wrote the Automation Script (automate.py)
This Python script uses the Docker SDK for Python to run all the container logic automatically:
import docker # type: ignore
import time
client = docker.from_env()
IMAGE_NAME = "my-custom-jenkins"
VOLUME_NAME = "jenkins_data_custom"
CONTAINER_NAME = "my-custom-jenkins"
DOCKERFILE_PATH = "."
# Step 2: Build the custom Jenkins image
print(f"Building image '{IMAGE_NAME}'...")
image, build_logs = client.images.build(path=DOCKERFILE_PATH, tag=IMAGE_NAME)
print("Image built successfully.")
# Step 3: Create volume
try:
volume = client.volumes.get(VOLUME_NAME)
print(f"Volume '{VOLUME_NAME}' already exists.")
except docker.errors.NotFound:
volume = client.volumes.create(name=VOLUME_NAME)
print(f"Volume '{VOLUME_NAME}' created.")
# Step 4: Run the container
print(f"Running container '{CONTAINER_NAME}'...")
try:
container = client.containers.run(
IMAGE_NAME,
name=CONTAINER_NAME,
ports={"8080/tcp": 8080},
volumes={VOLUME_NAME: {'bind': '/var/jenkins_home', 'mode': 'rw'}},
detach=True
)
except docker.errors.APIError as e:
print(f"Error: {e}")
print("Trying to remove existing container and retry...")
try:
old_container = client.containers.get(CONTAINER_NAME)
old_container.stop()
old_container.remove()
container = client.containers.run(
IMAGE_NAME,
name=CONTAINER_NAME,
ports={"8080/tcp": 8080},
volumes={VOLUME_NAME: {'bind': '/var/jenkins_home', 'mode': 'rw'}},
detach=True
)
except Exception as e2:
print(f"Failed to recover: {e2}")
exit(1)
print("Container is starting. Waiting 15 seconds for Jenkins to initialize...")
time.sleep(15) # Wait for Jenkins to generate password
# Step 5: Get the initial admin password
exec_log = container.exec_run("cat /var/jenkins_home/secrets/initialAdminPassword")
admin_password = exec_log.output.decode().strip()
print(f"\n Jenkins Initial Admin Password:\n{admin_password}\n")
Just run this script, and your Jenkins container is up and running.
🧱 Common Roadblocks I Hit
No project is complete without a few bumps along the way. Here are a couple that tripped me up:
❌ mypy Not Recognized in PowerShell
Even after installing mypy, I kept seeing errors like:
mypy: The term 'mypy' is not recognized...
Turns out, PowerShell doesn’t run scripts from the current directory by default. The fix? Run it like this:
.\mypy
Or better yet, add the script location to your PATH.
🐍 ModuleNotFoundError: No module named 'docker'
Even though Docker was installed and working, Python couldn’t find the Docker SDK. I had to run:
pip install docker
Always make sure you’re using the correct Python environment (virtualenvs help!).
✅ Final Thoughts
If you’re learning DevOps or just want to get better with Docker and automation, I highly recommend trying something similar. Small projects like these sharpen your skills and make bigger tools like Jenkins feel a lot less intimidating.
Check out the full repo here:
👉 https://github.com/Judewakim/CD-with-Docker-and-Jenkins
And if you liked this article, feel free to drop a comment or share it with someone diving into DevOps!
🧭 Follow My Cloud Journey
I regularly share hands-on cloud builds, automation tricks, and AWS-focused deep dives across the web:
🔗 LinkedIn — for cloud content, networking, and consulting
📖 Medium — where this and other walkthroughs live
👨💻 GitHub — for open-source tools and infra templates
🖥️ Dev.to — cross-posts and project writeups
Until next time — secure your containers and keep building 🔐🐳⚙️
Top comments (0)