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 latest in production (e.g., mariadb:11.2 not mariadb: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

Last updated 07 Apr 2026, 00:00 +0200. history

Was this page helpful?