Docker
Docker Tutorial And Cheat Sheet
Learn Docker for QA automation, including Jenkins, MySQL, Flask app containers, bind mounts, Dockerfiles, custom images, and Docker Hub workflows.
Welcome to the Docker cheat sheet and tutorial. This guide walks through Docker commands and practical examples for running existing images, using volumes, building custom images, and pushing images to Docker Hub.
For QA engineers and automation engineers, Docker is useful because it lets you run databases, CI/CD tools, test applications, and disposable environments without manually installing everything on your machine.
Table Of Contents
- Common Docker Commands
- Using Docker To Run Applications From Existing Images
- Running Jenkins In Docker
- Running MySQL In Docker
- Running A Flask To-Do App In Docker
- Using Docker Volumes
- Using Bind Mounts With Jenkins
- Using Bind Mounts With MySQL
- Using Bind Mounts With A Flask To-Do App
- Building Your Own Docker Images
- Pushing Images To Docker Hub
Common Docker Commands
| Command | What It Does |
|---|---|
docker run | Runs a container from an image. |
docker pull | Pulls an image from a Docker registry. |
docker build | Builds an image from a Dockerfile. |
docker ps | Lists running containers. |
docker stop | Stops a running container. |
docker rm | Removes a stopped container. |
docker images | Lists downloaded images. |
docker rmi | Removes an image. |
docker exec | Runs a command inside a running container. |
docker run
docker pull
docker build
docker ps
docker stop
docker rm
docker images
docker rmi
docker exec
Using Docker To Run Applications From Existing Images
Running applications in Docker containers gives you consistent environments, isolation, and easier setup. This section covers several examples: Jenkins for CI/CD, MySQL for database testing, and a custom Flask To-Do application for web app practice.
Each example highlights important Docker concepts such as port mapping, environment variables, container naming, detached mode, and data persistence.
Running Jenkins In Docker
Jenkins is a CI/CD tool used to automate build, test, and deployment workflows. Running Jenkins in Docker gives you an isolated CI/CD environment that you can start, stop, and recreate quickly.
Method 1: Run Jenkins With Specific Ports
First, pull the Jenkins image from Docker Hub. If you skip this step, Docker will automatically pull the image when you run the container.
docker pull jenkins/jenkins:lts
Run Jenkins and map the container ports to known host ports:
docker run --name jenkins-container -p 8080:8080 -p 50000:50000 jenkins/jenkins:lts
| Option | Description |
|---|---|
--name jenkins-container | Assigns a readable name to the container. |
-p 8080:8080 | Maps host port 8080 to container port 8080 so Jenkins is available at http://localhost:8080. |
-p 50000:50000 | Maps the Jenkins agent communication port. |
jenkins/jenkins:lts | Uses the long-term-support Jenkins image. |
Method 2: Run Jenkins With Automatic Port Mapping
Docker's -P flag maps exposed container ports to random available host ports. This is useful when you do not care which host port gets used.
docker run --name jenkins-container -P jenkins/jenkins:lts
After running the command, check which ports were assigned:
docker ps
Then open Jenkins at http://localhost:[random_port].
Naming Containers
The --name option gives a container a specific name. This makes containers easier to identify, stop, remove, and inspect. If you do not name a container, Docker generates a random name.
Running MySQL In Docker
MySQL is a relational database. Running MySQL in Docker lets you create a consistent and isolated database environment for database testing, API testing, and automation practice.
Run MySQL With Environment Variables And Port Mapping
docker run --name mysql-container -e MYSQL_ROOT_PASSWORD=my-secret-pw -p 3306:3306 -d mysql:latest
| Option | Description |
|---|---|
--name mysql-container | Assigns the name mysql-container to the running container. |
-e MYSQL_ROOT_PASSWORD=my-secret-pw | Sets the root password for MySQL using an environment variable. |
-p 3306:3306 | Maps host port 3306 to container port 3306. |
-d | Runs the container in detached mode in the background. |
mysql:latest | Uses the official MySQL image. Docker pulls it automatically if needed. |
Connect From MySQL Workbench
After running the MySQL container, you can connect to it from MySQL Workbench:
- Open MySQL Workbench and create a new connection.
- Use any connection name you prefer.
- Set hostname to
localhost. - Set port to
3306. - Set username to
root. - Set password to
my-secret-pw. - Click Test Connection and save the connection if successful.
Connect From Inside The Container
You can also connect directly from inside the container with docker exec:
docker exec -it mysql-container mysql -uroot -p
This opens an interactive terminal in the running mysql-container, starts the MySQL client, and logs in as root. Enter my-secret-pw when prompted.
Verify The MySQL Connection
Once connected, run:
SHOW DATABASES;
If the command lists databases, your MySQL connection is working.
Running A Flask To-Do App In Docker
This example uses a custom Flask To-Do application published on Docker Hub as supersqa/todo-app-flask-docker-demo. Running this app in Docker provides a consistent environment for testing and development.
Method 1: Automatic Port Mapping
The simplest option is to use -P, which maps the container's internal port to a random available port on the host.
docker run --name todo-app -P -d supersqa/todo-app-flask-docker-demo
| Option | Description |
|---|---|
--name todo-app | Assigns the name todo-app to the container. |
-P | Maps exposed container ports to random available host ports. |
-d | Runs the container in detached mode. |
supersqa/todo-app-flask-docker-demo | Uses the Flask To-Do app image from Docker Hub. |
Find the assigned port:
docker port todo-app
Docker may show output like:
0.0.0.0:32768->5151/tcp
In that example, open http://localhost:32768.
Method 2: Specific Port Mapping
To make the app available at a known port, map port 5151 explicitly:
docker run --name todo-app -p 5151:5151 -d supersqa/todo-app-flask-docker-demo
Then open:
http://localhost:5151
Method 3: Environment Variables And Volume
For more advanced configuration, pass environment variables and mount a host directory:
docker run --name todo-app -p 5151:5151 -e FLASK_ENV=development -v $(pwd)/app-data:/app/data -d supersqa/todo-app-flask-docker-demo
| Option | Description |
|---|---|
-e FLASK_ENV=development | Sets an environment variable inside the container. |
-v $(pwd)/app-data:/app/data | Mounts a host directory into the container for data persistence. |
Stop And Remove The To-Do App
docker stop todo-app
docker rm todo-app
Using Docker Volumes
Docker volumes persist data used by and generated inside containers. Without persistence, data stored in a container's writable layer can disappear when the container is removed.
Docker provides three common storage patterns:
| Type | Description |
|---|---|
| Anonymous volumes | Created without a specific name. Useful for temporary storage. |
| Named volumes | Explicitly named volumes that can be reused across containers. |
| Bind mounts | Mount a specific host directory or file into a container. Useful for development and direct access to files. |
This tutorial focuses on bind mounts because they are practical for development, testing, configuration files, databases, and app data.
Using Bind Mounts With Jenkins
Bind mounts let Jenkins store configuration and job data on the host so it survives container restarts and removal.
docker run --name jenkins-container -v $(pwd)/jenkins_home:/var/jenkins_home -p 8080:8080 -p 50000:50000 -d jenkins/jenkins:lts
| Option | Description |
|---|---|
-v $(pwd)/jenkins_home:/var/jenkins_home | Mounts jenkins_home from the current host directory into Jenkins' home directory inside the container. |
-p 8080:8080 | Makes Jenkins available at http://localhost:8080. |
-p 50000:50000 | Maps the Jenkins agent port. |
-d | Runs the container in detached mode. |
Using Bind Mounts With MySQL
Bind mounts are useful for persisting MySQL database files on the host.
docker run --name mysql-container -v $(pwd)/mysql_data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -p 3306:3306 -d mysql:latest
| Option | Description |
|---|---|
-v $(pwd)/mysql_data:/var/lib/mysql | Stores MySQL data in the host's mysql_data directory. |
-e MYSQL_ROOT_PASSWORD=my-secret-pw | Sets the root password. |
-p 3306:3306 | Maps MySQL port 3306 to the host. |
-d | Runs the container in detached mode. |
Using Bind Mounts With A Flask To-Do App
Bind mounts can also persist app data from a custom web app.
docker run --name todo-app-container -v $(pwd)/app-tasks:/app/tasks -p 5151:5151 -d supersqa/todo-app-flask-docker-demo
| Option | Description |
|---|---|
-v $(pwd)/app-tasks:/app/tasks | Mounts the host's app-tasks directory into the container at /app/tasks. |
-p 5151:5151 | Makes the app available at http://localhost:5151. |
-d | Runs the container in detached mode. |
Bind mounts are especially useful when containers need direct access to host files, configuration, database files, or development data.
Building Your Own Docker Images
Building custom Docker images lets you define your application's environment, dependencies, and runtime behavior. This helps applications run consistently across different machines and environments.
Docker Images And Dockerfiles
Docker images are the foundation of containers. They include the OS layer, libraries, dependencies, and application code needed to run the app.
A Dockerfile is a text file with instructions Docker uses to build an image. It makes the image creation process repeatable and consistent.
Create A Simple Dockerfile
Here is a basic Dockerfile for a Python Flask application:
# Use an official Python runtime as a parent image
FROM python:3.9-slim
# Set the working directory in the container
WORKDIR /app
# Copy the current directory contents into the container at /app
COPY . /app
# Install any needed packages specified in requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
# Make port 5000 available to the world outside this container
EXPOSE 5000
# Define environment variable
ENV NAME World
# Run app.py when the container launches
CMD ["python", "app.py"]
| Instruction | Description |
|---|---|
FROM python:3.9-slim | Uses a slim Python image as the base image. |
WORKDIR /app | Sets /app as the working directory. |
COPY . /app | Copies the current directory into the image. |
RUN pip install --no-cache-dir -r requirements.txt | Installs Python dependencies. |
EXPOSE 5000 | Documents that the app listens on port 5000. |
ENV NAME World | Sets an environment variable. |
CMD ["python", "app.py"] | Runs the Flask app when the container starts. |
Build The Docker Image
Run this command in the same directory as the Dockerfile:
docker build -t my-flask-app .
| Option | Description |
|---|---|
-t my-flask-app | Tags the image with the name my-flask-app. |
. | Sets the build context to the current directory. |
Dockerfile Best Practices
- Keep Dockerfiles simple and readable.
- Use official base images when possible.
- Start from minimal trusted base images to reduce image size and vulnerability surface.
- Minimize unnecessary layers where practical.
- Never hardcode secrets, passwords, tokens, or private keys in Dockerfiles.
- Use environment variables or secret management for sensitive configuration.
- Use multi-stage builds when a complex build process would otherwise make the final image large.
Use Environment Variables At Runtime
Environment variables can configure a container without rebuilding the image.
docker run -e NAME=OpenAI my-flask-app
The -e NAME=OpenAI option sets the NAME environment variable inside the container.
Install External Dependencies
If your app requires external dependencies, install them in the Dockerfile:
RUN pip install --no-cache-dir requests
The --no-cache-dir flag avoids storing unnecessary pip cache files in the image.
Pushing Images To Docker Hub
After building a custom Docker image, you can push it to Docker Hub for sharing or deployment.
docker push my-flask-app
Make sure you are logged in to Docker Hub before pushing.
For a real Docker Hub repository, the image usually needs to be tagged with your Docker Hub username and repository name:
docker tag my-flask-app your-dockerhub-username/my-flask-app:latest
docker push your-dockerhub-username/my-flask-app:latest
Conclusion
You have now covered the core Docker workflow:
- Running containers from existing images
- Mapping ports
- Naming containers
- Passing environment variables
- Using bind mounts for persistence
- Building custom Docker images with Dockerfiles
- Pushing images to Docker Hub
Docker simplifies application setup and creates consistent environments across machines. For QA automation, this means you can spin up test databases, CI/CD tools, demo apps, and disposable environments quickly and reliably.
Free Software Testing Fundamentals
If you are learning Docker as part of your QA or automation path, start with the testing foundations too.
The free Software Testing Fundamentals course covers:
- Testing basics, test cases, and bug reporting
- SQL and database testing
- API testing
- Jira and real team workflows
