Docker in Day-to-Day Work: From "It Works on My Machine" to Optimized Deploys
If you develop software, you've certainly heard (or even said) the classic phrase: "That's weird, it works on my machine". Docker was created exactly to eradicate this problem, standardizing the environment from development all the way to production. In this article, we are going to demystify Docker, understand the difference between it and Virtual Machines, and get our hands dirty with practical examples using Spring Boot and PostgreSQL.
1. What is Docker?
Docker is an open-source platform that makes it easier to create, deploy, and run applications within isolated environments called Containers.
A container packages your application's code along with all its dependencies (libraries, frameworks, runtime), ensuring that it runs exactly the same way on any infrastructure, whether on your personal laptop, a staging server, or in the cloud.
Docker vs. Virtual Machine (VM)
The most common initial confusion is thinking that Docker is just a "mini VM". Their architectures are fundamentally different:
Feature | Virtual Machine (VM) | Container (Docker) |
Architecture | Includes the app, binaries/libraries, and an entire Guest Operating System. | Includes only the app and its libraries. Shares the Host OS Kernel. |
Size | Heavy (Gigabytes). | Very lightweight (Megabytes). |
Boot Time | Slow (minutes, as it needs to boot a full OS). | Almost instantaneous (seconds or milliseconds). |
Isolation | Total isolation (Hardware virtualization). | Process isolation (Namespaces/Cgroups). |
2. How to Install Docker
Windows: Download and install Docker Desktop. It is highly recommended to have WSL 2 (Windows Subsystem for Linux) enabled on your machine for maximum performance.
Mac: Download and install Docker Desktop for Mac. It works flawlessly on both Intel and Apple Silicon (M1/M2/M3) chips.
Linux (Ubuntu/Debian): Installation via the terminal is the most common approach:
sudo apt-get update sudo apt-get install docker.io docker-compose-v2 sudo systemctl enable --now docker
3. The Main Docker Commands
Here is your essential terminal survival kit:
docker pull <image>: Downloads an image from Docker Hub (e.g.,docker pull postgres:16).docker build -t <name> .: Builds an image from a Dockerfile located in the current directory.docker run -p 8080:8080 -d <image>: Runs a container in the background (-d) mapping your local port to the container's port.docker ps: Lists running containers. Usedocker ps -ato see stopped ones as well.docker stop <container-id>: Gracefully stops a running container.docker rm <container-id>: Removes a container (it must be stopped first).docker logs -f <container-id>: Displays and follows the container's logs in real-time.docker exec -it <container-id> bash: Opens an interactive terminal inside a running container.
4. What is a Dockerfile? (Spring Boot Example)
The Dockerfile is the "recipe" for your image. It is a text file containing step-by-step instructions on how Docker should build your application's environment.
If you have a compiled Spring Boot application (app.jar), the Dockerfile using Java 21 would look like this:
# Uses an official Java 21 image (lightweight Alpine-based version)
FROM eclipse-temurin:21-jre-alpine
# Sets the working directory inside the container
WORKDIR /app
# Copies the .jar file from your host machine into the container
COPY target/my-spring-app.jar app.jar
# Exposes the port the Spring Boot app will run on
EXPOSE 8080
# The command that will be executed when the container starts
ENTRYPOINT ["java", "-jar", "app.jar"]
To run it:
docker build -t my-spring-app .docker run -p 8080:8080 -d my-spring-app
5. What is Docker Compose?
While a Dockerfile builds a single image, Docker Compose orchestrates multiple containers. It uses a YAML file (docker-compose.yml) to configure services, networks, and volumes, allowing you to spin up your entire infrastructure with a single command: docker compose up -d.
Example 1: PostgreSQL Only
Do you need a fast database for local testing without cluttering your Windows/Mac OS with Postgres installations? Use Compose:
services:
db:
image: postgres:16-alpine
container_name: my_postgres
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: password123
POSTGRES_DB: mydatabase
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata: # Keeps data saved even if the container is destroyed
Example 2: Spring Boot + PostgreSQL Together
Now let's connect the API we built in step 4 with the database. Compose creates an internal network where containers can talk to each other using their service names (Spring will find the DB at the URL jdbc:postgresql://db:5432/mydatabase).
services:
# Our Spring Boot API Service
api:
build: . # Builds the image by reading the Dockerfile in the current directory
container_name: spring_api
ports:
- "8080:8080"
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/mydatabase
- SPRING_DATASOURCE_USERNAME=admin
- SPRING_DATASOURCE_PASSWORD=password123
depends_on:
- db # Ensures the API only starts AFTER the database is up
# Database Service
db:
image: postgres:16-alpine
container_name: postgres_db
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: password123
POSTGRES_DB: mydatabase
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
By running docker compose up -d --build, Docker will build the Spring image, download the Postgres image, and spin both up connected to the same network!