Docker - Practical Handbook
Docker runs applications in repeatable, isolated containers. The core model is simple: an image is a template, a container is a running instance, a volume holds persistent data, and Compose describes a set of cooperating services.
When this handbook is useful: when running an existing project, building an image, developing locally, deploying a small application to a VPS, moving images between hosts, or troubleshooting ports, networking, storage and logs.
The handbook uses the modern docker compose command provided by the Compose plugin rather than the legacy standalone docker-compose command.
Related topics: Debian 13 - Desktop + Server Handbook, systemd, cron and Schedulers, Linux Permissions and Server Security, and nginx and reverse proxy.
Handbook map
- What Docker is
- Containers and images
- Ports and networking
- Volumes and persistent data
- Dockerfile and builds
- Docker Compose
- Deployment and updates
- Backups and cleanup
- Troubleshooting
- Security
1. What Docker is
Docker runs applications in containers. A container is an isolated process environment that shares the host kernel but can have its own filesystem, libraries, configuration, environment variables, ports, networks and resource limits. It is not a full virtual machine.
Linux host
│
├── Docker Engine
├── application container A
├── application container B
└── database container
Core concepts: image = application template, container = running image instance, Dockerfile = image build recipe, volume = persistent data, network = container connectivity, registry = image storage, Compose = multi-container application definition.
2. Docker vs virtual machines
A VM includes its own kernel and operating system. Containers share the host kernel and therefore start faster and usually use fewer resources.
Virtual machine: Host → Hypervisor → Guest Kernel + OS + app
Docker: Linux Host → Docker Engine → isolated application containers
Containers are especially useful for backend services, web applications, databases used in development, CI jobs and reproducible runtime environments.
3. Operating systems
Docker is fundamentally a Linux container technology. It runs natively on Linux distributions such as Debian, Ubuntu, Fedora, Rocky Linux and AlmaLinux. Docker Desktop on Windows and macOS uses a Linux environment underneath. FreeBSD does not use Docker as a native container technology; its native isolation mechanism is jails.
4. Check installation
docker --version
docker info
docker info shows engine version, images, containers, storage driver, runtimes and other daemon details.
5. Start and stop Docker
sudo systemctl start docker
sudo systemctl stop docker
sudo systemctl restart docker
sudo systemctl status docker
sudo systemctl enable --now docker
6. Docker without sudo
sudo usermod -aG docker "$USER"
newgrp docker
docker ps
Security note
Membership in the docker group is effectively root-equivalent on a normal Docker host. Treat it as privileged access.
7. First container
docker run hello-world
Docker checks for the image locally, pulls it if missing, creates a container, runs it and shows the program output.
8. Images
docker images
docker image ls
9. Pulling an image
docker pull nginx
docker pull nginx:1.28
docker pull postgres:17
10. Removing an image
docker rmi nginx
docker image rm nginx
docker rmi IMAGE_ID
docker rmi -f nginx
11. Running a container
docker run IMAGE
docker run -d nginx
-d means detached mode, so the container runs in the background.
12. Naming a container
docker run -d --name web nginx
docker stop web
docker start web
docker logs web
13. Listing containers
docker ps
docker ps -a
docker container ls
docker container ls -a
14. Start, stop, restart
docker start web
docker stop web
docker restart web
15. Removing a container
docker rm web
docker rm -f web
16. Ports
A container has its own network namespace. Publishing a port maps a host port to a container port.
docker run -d --name web -p 8080:80 nginx
HOST:CONTAINER
8080:80
Now http://localhost:8080 reaches port 80 inside the container.
Note: -p 8080:80 publishes the port on host interfaces according to Docker's networking configuration. If the service should remain local to the host, bind it explicitly to 127.0.0.1, as in the next example. Docker manages its own filtering/NAT rules, so host firewall behaviour must be considered together with Docker's rules.
17. Port only on localhost
docker run -d -p 127.0.0.1:8080:80 nginx
This is useful when nginx on the host acts as a reverse proxy and the container should not be directly reachable from the network.
18. Checking port mappings
docker port web
docker ps
19. Container logs
docker logs web
docker logs --tail 50 web
docker logs -f web
docker logs --tail 100 -f web
20. Entering a container
docker exec -it web bash
docker exec -it web sh
Use sh when the image does not contain Bash. Minimal images may not contain any shell.
21. Running a single command
docker exec web ls /etc
docker exec postgres pg_isready
22. Environment variables
docker run -d -e APP_ENV=production -e PORT=8080 myapp
23. .env file
APP_ENV=production
DATABASE_URL=postgres://app:secret@db/app
PORT=8080
docker run --env-file .env myapp
Do not commit secret-filled .env files to a public repository.
.env
.env.*
24. Container filesystem
Changes made in a container's writable layer disappear when that container is removed. Persistent state belongs in volumes, bind mounts, external databases or object storage.
25. Bind mount
docker run -d -p 8080:80 -v "$PWD/html:/usr/share/nginx/html" nginx
Host file changes are immediately visible inside the container.
26. --mount syntax
docker run -d --mount type=bind,source="$PWD/html",target=/usr/share/nginx/html nginx
27. Volumes
docker volume ls
docker volume create postgres-data
docker run -d --name db -v postgres-data:/var/lib/postgresql/data postgres:17
28. Where Docker stores volumes
On Linux Docker commonly stores managed volume data under /var/lib/docker/volumes/, but you should manage volumes through Docker commands rather than editing that directory directly.
29. Volume information
docker volume inspect postgres-data
30. Removing a volume
docker volume rm postgres-data
Removing a volume destroys the data stored there.
31. Docker networks
docker network ls
Docker normally provides bridge, host and none. User-defined bridge networks are the usual choice for multi-container applications.
32. Creating a network
docker network create app-network
33. Running containers on the same network
docker run -d --name db --network app-network postgres:17
docker run -d --name app --network app-network myapp
The application can address the database by container name, for example db:5432.
34. Inspect
docker inspect web
docker inspect -f '{{.State.Status}}' web
Inspect exposes configuration, networks, mounts, environment, ports, state, image and entrypoint.
35. Container statistics
docker stats
36. Processes in a container
docker top web
37. Dockerfile
FROM golang:1.25 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o app ./cmd/server
FROM debian:13-slim
WORKDIR /app
COPY --from=build /src/app /app/app
EXPOSE 8080
CMD ["/app/app"]
38. How to read a Dockerfile
FROM
Selects the base image or starts a new build stage.
WORKDIR
Sets the working directory for later instructions.
COPY
Copies files from the build context into the image.
RUN
Executes a command during image build and stores the result in a layer.
ENV
Defines an environment variable in the image/runtime.
EXPOSE
Documents an intended container port; it does not publish the port by itself.
CMD
Defines the default command/arguments for the container.
ENTRYPOINT
Defines the primary executable; CMD can then provide default arguments.
39. Building an image
docker build -t myapp .
40. Tagging an image
docker tag myapp myapp:1.0
Use explicit versions such as myapp:1.0 instead of relying only on latest.
41. Running your own image
docker run -d --name myapp -p 8080:8080 myapp:1.0
42. .dockerignore
.git
.gitignore
.env
node_modules
tmp
dist
*.log
.dockerignore keeps unnecessary files out of the build context.
43. Multi-stage build
Use one stage for compilation and a smaller stage for runtime. This avoids shipping compilers, source code and build caches in production images.
44. Even smaller Go image
FROM golang:1.25 AS builder
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -o app
FROM scratch
COPY --from=builder /src/app /app
ENTRYPOINT ["/app"]
A scratch image has no shell, so docker exec -it app sh will not work.
45. Docker Compose
Compose describes a multi-container application in one YAML file, commonly compose.yaml.
services:
app:
build: .
ports:
- "8080:8080"
environment:
DATABASE_URL: postgres://app:secret@db:5432/app
depends_on:
- db
db:
image: postgres:17
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: app
volumes:
- postgres-data:/var/lib/postgresql/data
volumes:
postgres-data:
46. Starting Compose
docker compose up
docker compose up -d
47. Build and run
docker compose up -d --build
48. Compose status
docker compose ps
49. Compose logs
docker compose logs
docker compose logs -f
docker compose logs -f app
50. Stopping Compose
docker compose stop
docker compose start
51. Removing Compose containers
docker compose down
Named volumes are preserved by default.
52. Removing Compose with volumes
docker compose down -v
This can delete database data. Use with care.
53. Restarting one service
docker compose restart app
54. Shell in a Compose container
docker compose exec app sh
docker compose exec app bash
55. Running a one-off command
docker compose exec app ./app migrate
docker compose exec app npm test
56. Validating Compose
docker compose config
This shows the effective configuration after variable interpolation and merges.
57. Compose project names
docker compose -p example-site up -d
Project names influence generated container/network names.
58. Restart policy
services:
app:
image: myapp
restart: unless-stopped
Common values are no, always, on-failure and unless-stopped.
59. Healthcheck
services:
app:
image: myapp
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 3
60. Memory limits
docker run --memory=512m myapp
docker run --cpus=1.0 myapp
61. Copying files to and from a container
docker cp config.json web:/app/config.json
docker cp web:/app/report.txt ./report.txt
62. Exporting an image - docker save
docker save myapp:1.0 -o myapp.tar
docker load -i myapp.tar
63. Compressing an image archive
docker save myapp:1.0 | gzip > myapp.tar.gz
gzip -dc myapp.tar.gz | docker load
64. docker save vs docker export
docker save
Exports an image with its layers, tags and metadata. Use it for moving images.
docker export
Exports the filesystem of a container. It does not preserve image history/configuration like save/load.
65. Rule
Move application image: docker save / docker load
Dump container filesystem: docker export / docker import
66. Registry
A registry stores images so hosts can push and pull them. Examples: Docker Hub, GitHub Container Registry, GitLab Container Registry and private registries.
67. Logging in to a registry
docker login
docker login ghcr.io
68. Pushing an image
docker tag myapp:1.0 username/myapp:1.0
docker push username/myapp:1.0
docker pull username/myapp:1.0
69. Typical deployment through a registry
docker build -t ghcr.io/user/myapp:1.2.0 .
docker push ghcr.io/user/myapp:1.2.0
On the VPS:
docker pull ghcr.io/user/myapp:1.2.0
docker compose up -d
70. Typical deployment without a registry
docker build -t myapp:1.0 .
docker save myapp:1.0 | gzip > myapp.tar.gz
scp myapp.tar.gz user@server:/tmp/
On the server:
gzip -dc /tmp/myapp.tar.gz | docker load
docker compose up -d
71. Best model for small projects
A simple small-project layout is source + Dockerfile + compose.yaml in Git, with Docker and nginx on the VPS.
Variant A - build on the server
git pull
docker compose up -d --build
Variant B - prebuilt images
Build in CI/local → push to registry → pull and restart on the VPS.
72. Docker + nginx reverse proxy
services:
app:
build: .
ports:
- "127.0.0.1:8080:8080"
restart: unless-stopped
server {
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
73. Updating an application
cd /srv/myapp
git pull
docker compose up -d --build
74. Updating an image from a registry
docker compose pull
docker compose up -d
75. Checking logs after deployment
docker compose logs --tail 100 -f
76. Backing up a volume
docker run --rm -v postgres-data:/data -v "$PWD:/backup" alpine tar czf /backup/postgres-data.tar.gz -C /data .
77. Restoring a volume
docker volume create postgres-data
docker run --rm -v postgres-data:/data -v "$PWD:/backup" alpine sh -c 'cd /data && tar xzf /backup/postgres-data.tar.gz'
78. Database backup
docker exec db pg_dump -U app app > backup.sql
cat backup.sql | docker exec -i db psql -U app app
For databases, logical backups are often safer than copying live database files.
79. Prune - cleanup
docker system df
80. Removing unused resources
docker system prune
docker system prune -a
-a is more aggressive and removes all unused images.
81. Volume prune
docker volume prune
Volumes may contain important data.
82. Build cache
docker builder du
docker builder prune
83. Common problems
Port already in use
sudo ss -lntp | grep ':8080'
Stop the conflicting service or publish a different host port.
84. Container exits immediately
docker ps -a
docker logs NAME
A container runs only while its main process is running.
85. Bash is unavailable
docker exec -it app sh
Minimal images may not contain Bash. scratch images may contain no shell at all.
86. Code changed but application is old
docker compose up -d --build
87. Docker uses old cache
docker build --no-cache -t myapp .
docker compose build --no-cache
docker compose up -d
88. Containers cannot communicate
Do not use localhost to reach another container. Use the Compose service name, e.g. db:5432.
89. localhost - important rule
on host: localhost = host
inside container: localhost = that container
another container: use service/container name
90. Container cannot reach a host service
On Linux, host access is different from Docker Desktop. Prefer explicit interfaces, shared networks, or move the dependency into Compose when practical.
91. Step-by-step debugging
docker ps -a
docker logs app
docker inspect app
docker exec -it app sh
Inside the container inspect environment, process list and filesystem. For network issues inspect Docker networks.
92. Compose debugging
docker compose ps
docker compose logs
docker compose config
docker compose exec app sh
93. Security
Do not bake secrets into Dockerfiles or images. Pass them at runtime via protected environment/configuration or a proper secret-management mechanism.
94. Do not use latest for critical services
Pin meaningful versions such as postgres:17 or even an exact patch version when reproducibility matters.
95. Do not expose databases to the Internet unnecessarily
If only the application needs PostgreSQL, keep it on the internal Compose network and omit ports:.
96. Run as non-root
RUN useradd -r -u 10001 appuser
USER appuser
CMD ["/app/app"]
97. Read-only filesystem
docker run --read-only myapp
read_only: true
98. docker run --rm
docker run --rm alpine echo hello
The container is automatically removed after it exits.
99. Temporary Linux shell
docker run --rm -it debian:13 bash
100. Testing different software versions
docker run --rm -it node:24 bash
docker run --rm -it python:3.14 bash
docker run --rm -it golang:1.25 bash
101. Overview of important commands
Containers
docker ps
docker ps -a
docker run IMAGE
docker stop NAME
docker restart NAME
docker rm NAME
docker logs NAME
docker exec -it NAME sh
docker inspect NAME
docker stats
Images
docker images
docker pull IMAGE
docker build -t NAME .
docker rmi IMAGE
docker tag SOURCE TARGET
docker save IMAGE -o image.tar
docker load -i image.tar
Volumes
docker volume ls
docker volume create NAME
docker volume inspect NAME
docker volume rm NAME
docker volume prune
Networks
docker network ls
docker network create NAME
docker network inspect NAME
docker network rm NAME
Compose
docker compose up -d
docker compose up -d --build
docker compose down
docker compose ps
docker compose logs -f
docker compose pull
docker compose exec SERVICE sh
docker compose config
102. Typical Go + Docker project
myapp/
├── cmd/server/main.go
├── internal/
├── static/
├── templates/
├── go.mod
├── go.sum
├── Dockerfile
├── compose.yaml
└── .dockerignore
103. Compose for a Go application
services:
app:
build: .
restart: unless-stopped
ports:
- "127.0.0.1:8080:8080"
environment:
APP_ENV: production
104. Go + PostgreSQL
Put app and db on the same Compose network, use db as the database host, persist database data in a named volume, and avoid exposing port 5432 unless external access is actually needed.
105. What should be in Git
Dockerfile
compose.yaml
.dockerignore
.env.example
application source code
Do not commit .env, secrets, database backups or image tar archives.
106. .env.example
APP_ENV=production
DATABASE_URL=
OPENROUTER_API_KEY=
cp .env.example .env
107. Typical local workflow
docker compose build
docker compose up -d
docker compose logs -f app
docker compose up -d --build
108. Typical local → VPS workflow
git add .
git commit -m "Add feature"
git push
On the VPS:
cd /srv/myapp
git pull
docker compose up -d --build
docker compose logs --tail 100
109. Typical registry workflow
docker build -t ghcr.io/user/myapp:1.5.0 .
docker push ghcr.io/user/myapp:1.5.0
On the VPS:
docker compose pull
docker compose up -d
110. Updating without long downtime
A single-instance Compose deployment may have a short interruption during replacement. True zero-downtime requires multiple instances/load balancing or more advanced deployment tooling.
111. Check how much space Docker uses
docker system df
docker system df -v
112. Where Docker stores data
docker info | grep "Docker Root Dir"
The default root is commonly /var/lib/docker.
113. Do not manually copy /var/lib/docker
Use image export/registry, volume backups, database dumps and Git instead of copying the Docker engine's internal directory.
114. Migrating an application between servers
Move the repository, secrets/config, database/volume backups, then pull or rebuild images and recreate containers.
115. What is actually worth exporting
source code → Git
Dockerfile/compose.yaml → Git
images → registry or docker save
secrets → protected backup
database → logical dump
volumes → backup
Treat containers as disposable runtime instances.
116. Docker's most important philosophy
A container should be disposable and reproducible. If deleting and recreating it destroys important data, your persistence design is wrong.
117. Complete small deployment example
A common small VPS stack is nginx on the host, the application on 127.0.0.1:8080 in Docker, secrets in .env, and source/compose files under /srv/myapp.
cd /srv/myapp
git pull
docker compose up -d --build
docker compose ps
docker compose logs --tail 100
118. Useful aliases
alias dps='docker ps'
alias dpa='docker ps -a'
alias di='docker images'
alias dc='docker compose'
alias dcl='docker compose logs -f'
alias dcu='docker compose up -d'
alias dcd='docker compose down'
119. Help
docker --help
docker run --help
docker compose --help
docker compose up --help
120. Minimal commands to remember
docker ps
docker ps -a
docker images
docker run
docker stop
docker start
docker rm
docker logs
docker exec -it NAME sh
docker inspect
docker build -t NAME .
docker pull
docker compose up -d
docker compose up -d --build
docker compose down
docker compose logs -f
docker system df
docker system prune
121. Minimal project workflow
First run
git clone REPO
cd PROJECT
cp .env.example .env
docker compose up -d --build
Check
docker compose ps
docker compose logs --tail 100
Update
git pull
docker compose up -d --build
Debug
docker compose logs -f app
docker compose exec app sh
Stop
docker compose down
122. Real-life examples
Example 1 - quick nginx
docker run -d --name nginx-test -p 8080:80 nginx
docker logs nginx-test
docker rm -f nginx-test
Example 2 - temporary Debian
docker run --rm -it debian:13 bash
Example 3 - PostgreSQL
docker volume create pgdata
docker run -d --name postgres -e POSTGRES_PASSWORD=secret -v pgdata:/var/lib/postgresql/data postgres:17
Example 4 - export image to another computer
docker build -t web-monitor:1.0 .
docker save web-monitor:1.0 | gzip > web-monitor.tar.gz
scp web-monitor.tar.gz server:/tmp/
Example 5 - Go application on VPS
git clone git@github.com:user/app.git
cd app
docker compose up -d --build
docker compose logs -f app
123. Docker - mental map
Dockerfile → docker build → IMAGE → docker run → CONTAINER
CONTAINER uses ports + env + networks + volumes
compose.yaml → docker compose up → multiple coordinated services
124. What you should know after this handbook
You should understand image vs container, engine lifecycle, ports, logs, exec, volumes, networks, Dockerfiles, image builds, Compose, deployment, image transfer, backups, cleanup, troubleshooting and basic security.
125. Cheat sheet - one screen
status
docker ps
docker ps -a
docker images
run
docker run -d --name app -p 8080:8080 image
logs
docker logs -f app
shell
docker exec -it app sh
stop/start
docker stop app
docker start app
docker restart app
remove
docker rm -f app
docker rmi image
build
docker build -t app:1.0 .
export image
docker save app:1.0 -o app.tar
import image
docker load -i app.tar
compose
docker compose up -d
docker compose up -d --build
docker compose ps
docker compose logs -f
docker compose down
disk usage
docker system df
cleanup
docker system prune
126. Most important things to remember
- Image is a template; container is a running instance. 2. Containers should be disposable. 3. Keep persistent data in volumes or external storage. 4. Keep source and infrastructure config in Git. 5. Use Compose for multi-service apps. 6. Start troubleshooting with
docker logs. 7. Usedocker inspectfor details. 8. Use save/load for images. 9. Export/import is for container filesystems and is rarely the right migration tool. 10. A small VPS usually needs only Git + Docker + Compose + nginx + backups.
127. Documentation and sources
Official Docker documentation is the primary reference for Engine, Build and Compose behaviour:
- Docker Engine
https://docs.docker.com/engine/ - Install Docker Engine on Debian
https://docs.docker.com/engine/install/debian/ - Docker Compose
https://docs.docker.com/compose/ - Docker Build
https://docs.docker.com/build/ - Storage
https://docs.docker.com/engine/storage/ - Linux post-installation and
dockergroup privileges
https://docs.docker.com/engine/install/linux-postinstall/
End
For small private projects you usually do not need Kubernetes or Swarm. Debian + Git + Docker + Compose + nginx + Let's Encrypt + regular backups is a perfectly sensible stack.