Docker Basics for VPS Users
Get started with Docker on your GoZen VPS. Install, run containers, and manage services with Docker Compose.
Docker lets you run applications in isolated containers. Each container has its own filesystem, network, and dependencies. No conflicts between apps, easy upgrades, and reproducible deployments. If you’re self-hosting anything on a GoZen VPS, Docker is the standard way to do it.
Installing Docker
SSH into your GoZen VPS (Ubuntu LTS recommended):
# Remove old Docker packages if any
sudo apt remove docker docker-engine docker.io containerd runc 2>/dev/null
# Install Docker using the official script
curl -fsSL https://get.docker.com | sudo sh
# Add your user to the docker group (so you don't need sudo for every command)
sudo usermod -aG docker $USER
# Install Docker Compose plugin
sudo apt install docker-compose-plugin -y
# Log out and back in for group changes to take effect
exit
Verify the installation:
docker --version
# Docker version 27.x.x
docker compose version
# Docker Compose version v2.x.x
Core Concepts
Image: a read-only template. Think of it as a snapshot of an application and all its dependencies. You pull images from Docker Hub.
Container: a running instance of an image. You can have multiple containers from the same image.
Volume: persistent storage. Container filesystems are temporary. Volumes survive container restarts and rebuilds.
Network: containers can talk to each other through Docker networks without exposing ports to the outside world.
Running Your First Container
# Pull and run an Nginx web server
docker run -d --name my-nginx -p 8080:80 nginx
# -d: run in background (detached)
# --name: give it a name
# -p 8080:80: map port 8080 on the host to port 80 in the container
Visit http://your-server-ip:8080 to see the Nginx welcome page.
Managing Containers
# List running containers
docker ps
# List all containers (including stopped)
docker ps -a
# Stop a container
docker stop my-nginx
# Start a stopped container
docker start my-nginx
# View container logs
docker logs my-nginx
# Follow logs in real-time
docker logs -f my-nginx
# Execute a command inside a running container
docker exec -it my-nginx bash
# Remove a container (must be stopped first)
docker rm my-nginx
# Remove a container and stop it in one command
docker rm -f my-nginx
Docker Compose (The Real Way)
Running individual docker run commands gets messy. Docker Compose lets you define multiple containers in a single YAML file and manage them together.
Example: WordPress + MySQL + Redis
Create a project directory:
mkdir ~/wordpress && cd ~/wordpress
Create docker-compose.yml:
services:
db:
image: mariadb:11
restart: always
volumes:
- db_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: change_this_root_pw
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: change_this_db_pw
redis:
image: redis:7-alpine
restart: always
wordpress:
image: wordpress:latest
restart: always
ports:
- "8080:80"
volumes:
- wp_data:/var/www/html
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: change_this_db_pw
depends_on:
- db
- redis
volumes:
db_data:
wp_data:
Start everything:
docker compose up -d
Docker pulls all images, creates the network, and starts the containers. WordPress is available at http://your-server-ip:8080.
Common Compose Commands
# Start all services
docker compose up -d
# Stop all services
docker compose down
# Stop and remove volumes (deletes all data!)
docker compose down -v
# View logs for all services
docker compose logs
# View logs for a specific service
docker compose logs wordpress
# Restart a specific service
docker compose restart wordpress
# Pull updated images
docker compose pull
# Pull and restart with new images
docker compose pull && docker compose up -d
# Check status
docker compose ps
Volumes and Persistent Data
Containers are ephemeral. When you remove a container, its filesystem is gone. Volumes keep your data safe.
# Named volume (Docker manages the location)
volumes:
- db_data:/var/lib/mysql
# Bind mount (you choose the location)
volumes:
- ./config:/etc/myapp/config
Named volumes are preferred for databases and application data. Docker manages them, and they’re easy to back up:
# List all volumes
docker volume ls
# Inspect a volume (see where it's stored)
docker volume inspect wordpress_db_data
# Back up a volume
docker run --rm -v wordpress_db_data:/data -v $(pwd):/backup alpine tar czf /backup/db-backup.tar.gz /data
Networking
By default, Docker Compose creates a network for each project. Containers in the same Compose file can reach each other by service name:
# WordPress connects to the database using hostname "db"
WORDPRESS_DB_HOST: db
To expose a container to the outside world, map ports:
ports:
- "8080:80" # host:container
- "3306:3306" # expose MySQL (only if needed externally)
Only expose ports you need. Databases should almost never be exposed.
Cleaning Up
Docker accumulates images, stopped containers, and unused volumes:
# Remove all stopped containers
docker container prune
# Remove unused images
docker image prune
# Remove unused volumes (careful - this deletes data)
docker volume prune
# Nuclear option: remove everything unused
docker system prune -a
Security Basics
- Don’t run containers as root inside the container when possible. Many images support running as a non-root user
- Don’t expose database ports to the internet. Keep them internal to the Docker network
- Keep images updated:
docker compose pull && docker compose up -d - Use specific image tags instead of
latestin production (e.g.,mariadb:11.2notmariadb:latest) - Set resource limits to prevent one container from consuming all server resources:
services:
app:
image: myapp
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
What to Do Next
- Hosting Nextcloud on GoZen - full Docker deployment walkthrough
- Set Up a Reverse Proxy with Nginx - put your containers behind HTTPS
- Server Hardening Basics - secure the host running Docker
Last updated 07 Apr 2026, 00:00 +0200.