Summary
Docker packages an application and the environment it needs into a container, so the same service can run more consistently on a laptop, a CI runner, and a server. The usual reason people search for Docker is simple: “it works on my machine, but not on the server.” Docker does not remove every deployment problem, but it makes the runtime, libraries, ports, and startup commands easier to describe and repeat.
This guide keeps the scope practical: what Docker images and containers are, how Docker differs from a virtual machine, how to install Docker Engine on Ubuntu, when Docker Desktop is used on Windows/macOS, how to read a basic Dockerfile and Compose file, and which mistakes tend to cause real confusion when a beginner moves from the first hello-world command to a small development environment.
Checked against the Korean source article, Docker official documentation, Docker Engine/Desktop release notes, Docker Compose documentation, Ubuntu package information, and the Podman official site. Docker installation commands and package names can change by distribution and version, so use the official docs as the final check before changing a real server.
In this article
- Background
- What was checked
- Docker concepts and installation
- Best practices
- Common mistakes and troubleshooting
- FAQ
- Conclusion
- References
Background
A common deployment failure starts with a vague sentence: “It runs locally, but the server fails.” The cause is often a difference in OS packages, runtime versions, environment variables, network ports, file paths, or manually edited server state.
Docker addresses this by building an image that contains the files and settings needed to start the application, then running that image as a container. For development, CI/CD, and repeatable test environments, this gives teams a clearer unit to build, run, stop, and replace.
- Development and production environments can be described more consistently.
- Application startup becomes easier to automate.
- CI/CD pipelines can build and test the same image shape repeatedly.
- Multiple services can be started together with Docker Compose.
What was checked
| Source checked | What it affects in this guide |
|---|---|
| Docker concept docs | Image, container, registry, Dockerfile, and Compose explanations |
| Docker Engine Ubuntu install docs | Official APT repository flow and docker-ce package family |
| Docker Engine release notes | Current Engine release line and the need to verify version-specific behavior |
| Docker Desktop release notes | Desktop updates may roll out gradually and older versions may not always be available |
| Docker Compose docs | Modern Compose is commonly used as docker compose, a Docker CLI plugin |
| Ubuntu package information | docker.io exists as a separate Ubuntu distribution package |
| Podman official site | Podman is a Docker-adjacent alternative worth comparing in some Linux/server contexts |
Docker concepts and installation
What is Docker?
Docker is a container-based application platform. A container runs an application process in an isolated environment, but unlike a full virtual machine it does not boot a separate guest operating system for every application.
| Term | Meaning |
|---|---|
| Image | A read-only template containing files, libraries, and configuration for an application |
| Container | A running instance created from an image |
| Docker Engine | The runtime that creates and runs containers |
| Docker CLI | The command-line tool used for commands such as docker run, docker build, and docker ps |
| Registry | A repository for storing and distributing images, such as Docker Hub |
| Docker Compose | A tool for defining multiple container services in a YAML file and running them together |
Docker containers vs. virtual machines
| Area | Virtual machine | Docker container |
|---|---|---|
| Isolation model | Runs a guest OS on a hypervisor | Shares the host OS kernel and isolates processes |
| Startup | Usually slower | Usually faster |
| Resource use | Heavier | Lighter for many application workloads |
| Typical use | OS-level isolation and strong VM boundaries | Application packaging, deployment, and consistent dev environments |
| Examples | VMware, VirtualBox, Hyper-V | Docker, Podman |
This does not mean a container is automatically safer than a VM. Containers share the host kernel, so production use still requires careful checks around permissions, mounted directories, exposed ports, image trust, and Docker socket access.
Installing Docker Engine on Ubuntu
On Ubuntu, Docker’s official documentation recommends adding Docker’s APT repository and installing the Docker Engine packages from there. Ubuntu’s own docker.io package can also exist, but when you want the official Docker Engine, Buildx, and Compose plugin flow, the official repository is often the clearer path.
| Package path | What it means | When it fits |
|---|---|---|
docker-ce |
Docker Community Edition packages from Docker’s official repository | You want to follow Docker’s current official install guide and plugin package names |
docker.io |
Docker package from Ubuntu’s distribution repository | Your organization intentionally follows Ubuntu distribution packages and update policy |
Before installing on an existing server, check whether old packages such as docker.io, docker-compose, docker-compose-v2, podman-docker, containerd, or runc are already present. Docker’s docs list conflicting packages, but on a real server you should check running containers and package dependencies before removing anything.
# 1. Update package metadata and install prerequisites
sudo apt update
sudo apt install -y ca-certificates curl
# 2. Create the keyring directory
sudo install -m 0755 -d /etc/apt/keyrings
# 3. Add Docker's official GPG key
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# 4. Register Docker's official APT repository
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
# 5. Update package metadata again
sudo apt update
# 6. Install Docker Engine and related plugins
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Then verify the installation:
sudo docker run hello-world
If the installation is normal, Docker downloads a test image and prints a Hello from Docker! message.
Running Docker without sudo
On Linux, you can add your current user to the docker group if you do not want to type sudo for every Docker command.
sudo usermod -aG docker $USER
newgrp docker
docker run hello-world
This is convenient for a personal development machine, but it is not a harmless permission change. Docker group access can effectively provide high privileges on the host. Treat a production server differently from a local laptop.
Docker Desktop on Windows and macOS
On Windows and macOS, most beginners install Docker Desktop. It bundles Docker Engine, Docker CLI, Docker Compose, and GUI management features. The important caveat is that Windows/macOS do not run Linux containers in exactly the same way as a native Linux server; Docker Desktop uses a virtualization layer, and Windows commonly uses a WSL 2 based environment.
docker version
docker run hello-world
Basic Docker commands
# Run an Ubuntu container with an interactive shell
docker run -it ubuntu bash
# Show running containers
docker ps
# Show all containers
docker ps -a
# Show images
docker images
# Stop and remove a container
docker stop <container_id>
docker rm <container_id>
# Remove an image
docker rmi <image_id>
Image and container execution flow
Write a Dockerfile
↓
Build an image with docker build
↓
Run a container with docker run
↓
Check state with docker logs / docker exec
↓
Clean up with docker stop / docker rm
docker run -d --name sample-nginx -p 8080:80 nginx:stable
curl http://localhost:8080
docker stop sample-nginx
docker rm sample-nginx
A simple Dockerfile example
FROM nginx:stable
COPY ./index.html /usr/share/nginx/html/index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Docker Test</title>
</head>
<body>
<h1>Hello Docker</h1>
</body>
</html>
docker build -t sample-nginx-page .
docker run -d --name sample-page -p 8080:80 sample-nginx-page
curl http://localhost:8080
docker stop sample-page
docker rm sample-page
Docker Compose example
With Docker Compose, multiple services can be described in one YAML file. In current Docker environments, the common command shape is docker compose, not always the older standalone docker-compose binary.
services:
postgres:
image: postgres:16
container_name: sample-postgres
ports:
- "5432:5432"
environment:
POSTGRES_DB: sampledb
POSTGRES_USER: sampleuser
POSTGRES_PASSWORD: "****"
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
docker compose up -d
docker compose down
# Be careful: this removes the named volume and deletes persisted database data.
docker compose down -v
Best practices
Use official and trusted images
Docker Hub contains many images, but not every image is maintained, safe, or appropriate for production. Start with official images or trusted vendor images whenever possible.
Pin image tags clearly
Avoid relying only on latest in production-like environments. A clear tag such as postgres:16 reduces unexpected upgrades.
Separate environment-specific configuration
Do not bake every environment-specific value into the image. Put database hosts, ports, modes, and similar settings into Compose environment values, .env files, or your deployment system’s configuration mechanism. For sensitive values, use a secrets mechanism suitable for your environment.
Manage volumes and networks deliberately
If a container stores data that must survive container replacement, use a volume or external storage. If multiple services communicate through Compose, understand that service names can be used as network names inside the Compose network.
Review security before production use
- Container user and privilege level
- Host directory mount scope
- Docker socket exposure
- Image vulnerability scanning
- Which ports are published to the host or internet
- Logging and monitoring
Compare Docker and Podman when policy requires it
Docker is the most familiar starting point for many developers, especially with Docker Desktop and Compose-based local development. In Linux server environments, rootless containers, daemonless operation, or organizational security policy may make Podman worth comparing.
| Area | Docker | Podman |
|---|---|---|
| Usability | Large Docker Desktop, Docker Engine, and Compose ecosystem | Docker-like CLI experience |
| Daemon model | Centered on the Docker daemon | Known for a daemonless model |
| Good fit | Local development standardization, Compose workflows, Docker Hub ecosystem | Rootless container workflows, Linux-server-centered operation, specific security policies |
Common mistakes and troubleshooting
Confusing docker.io and docker-ce
If a guide says apt install docker.io but Docker’s official docs say docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin, do not treat them as identical paths. First decide whether you are following Ubuntu’s package policy or Docker’s official repository.
Adding every user to the docker group without thinking
Adding a user to the docker group fixes many local permission errors, but it can also grant powerful host access. On a server, check the permission policy before applying the convenience fix.
Treating containers like manually maintained VMs
If you keep entering a container and editing files by hand, the environment becomes hard to reproduce. Prefer Dockerfiles, Compose files, environment variables, and rebuildable images.
Keeping persistent data only inside the container
A container can be removed and recreated. Databases, uploads, and other persistent data should live in volumes or external storage.
FAQ
What should I do if Docker on Ubuntu says permission denied?
If you see permission denied while trying to connect to the Docker daemon socket, the current user probably cannot access the Docker daemon socket. For a local development machine, adding the user to the docker group and starting a new login session can fix it.
sudo usermod -aG docker $USER
newgrp docker
What should I check when docker compose does not work?
Check whether the Compose CLI plugin is installed.
docker compose version
sudo apt install docker-compose-plugin
Can I use Docker on a Linux server without Docker Desktop?
Yes. Linux servers commonly use Docker Engine and the Compose plugin directly. Docker Desktop is mainly a convenient developer tool for Windows/macOS environments.
How do I verify that Docker is installed?
docker version
docker run hello-world
docker run hello-world is the simplest functional check because it verifies that Docker can pull an image and run a container.
Conclusion
Docker is useful because it turns an application runtime into something you can build, run, stop, and recreate. On Ubuntu, the official Docker repository path is often the clearest way to install Docker Engine and the Compose plugin; on Windows and macOS, Docker Desktop is the usual beginner-friendly route.
The key is not only installing Docker successfully. Check package source, permissions, image tags, volumes, published ports, and production security settings before treating a local example as an operations-ready setup.
References
- Docker Docs: What is Docker?
- Docker Docs: Install Docker Engine on Ubuntu
- Docker Docs: Linux post-installation steps for Docker Engine
- Docker Docs: Docker Desktop
- Docker Docs: Docker Compose installation
- Docker Docs: Dockerfile reference
- Docker Docs: Docker Engine release notes
- Docker Docs: Docker Desktop release notes
- Ubuntu Packages: docker.io
- Podman Official Site
Read the original Korean post.
Please show some love to Korean, too.