TutorialsAugust 4, 20264 min read

How to Install Docker Compose and Run Your First Stack

Use Docker Compose to define and run a multi-container app from one YAML file. Build a WordPress + MySQL stack from scratch.

NBy Nxeon

Docker Compose lets you define a whole application — web server, database, cache — in a single YAML file and start it all with one command. This guide confirms Compose is installed, explains the file format, and walks through launching a real WordPress + MySQL stack.

Compose comes with modern Docker

If you installed Docker from the official repository (see installing Docker on Ubuntu 24.04), Compose v2 is already there as a subcommand. Check it:

docker compose version

Note the space: it is docker compose, not the old hyphenated docker-compose. If the command is missing, install the plugin:

sudo apt install docker-compose-plugin -y

Anatomy of a compose file

A Compose file describes services (containers), volumes (persistent storage), and networks. Compose automatically puts all services on a shared network where they can reach each other by service name — so db becomes a hostname. That single feature is what makes multi-container apps painless.

Build a WordPress + MySQL stack

Create a project directory and a compose.yaml:

mkdir ~/wordpress && cd ~/wordpress

Put this in compose.yaml:

services:
  db:
    image: mysql:8.0
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wpuser
      MYSQL_PASSWORD: change-me-strong
      MYSQL_ROOT_PASSWORD: change-me-root
    volumes:
      - db_data:/var/lib/mysql

  wordpress:
    image: wordpress:latest
    restart: unless-stopped
    depends_on:
      - db
    ports:
      - "127.0.0.1:8080:80"
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wpuser
      WORDPRESS_DB_PASSWORD: change-me-strong
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wp_data:/var/www/html

volumes:
  db_data:
  wp_data:

Notice WORDPRESS_DB_HOST: db:3306 — it reaches the database by the service name db. And the port is bound to 127.0.0.1 so it is not exposed publicly; you will put Nginx in front.

Start the stack

From the project directory:

docker compose up -d

Compose pulls both images, creates the volumes and network, and starts everything in the background. Watch it come up:

docker compose ps
docker compose logs -f
Building a website with Claude AI inside the Nxeon control panel
Building a website with Claude AI inside the Nxeon control panel

WordPress is now answering on 127.0.0.1:8080. Front it with an Nginx reverse proxy and free HTTPS from Certbot to serve it on your domain.

Everyday Compose commands

  • Start / recreate: docker compose up -d
  • Stop, keep data: docker compose stop
  • Stop and remove containers: docker compose down
  • Remove containers and volumes (deletes data): docker compose down -v
  • Update images: docker compose pull && docker compose up -d
  • Run a command in a service: docker compose exec db mysql -u root -p

Persistence is in volumes

The named volumes db_data and wp_data keep your database and uploads even when containers are recreated. docker compose down is safe; only down -v deletes the volumes. Always back these up before major changes.

Where to go next

Compose is the foundation for self-hosting nearly anything: databases, n8n, Nextcloud, monitoring stacks, and more. For a point-and-click way to manage your Compose stacks, install Portainer.

Make dependencies reliable with health checks

depends_on only waits for a container to start, not for the service inside it to be ready — so an app can race ahead of a database that is still booting. Add a health check so Compose waits for genuine readiness:

  db:
    image: mysql:8.0
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      retries: 5

Then depend on that health:

  wordpress:
    depends_on:
      db:
        condition: service_healthy

Keep secrets in an .env file

Rather than hard-coding passwords in the YAML, put them in a .env file beside the compose file; Compose substitutes variables automatically:

MYSQL_PASSWORD=change-me-strong
    environment:
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}

Add .env to .gitignore so secrets never reach version control. This keeps one clean, shareable compose file with environment-specific values kept separate.

FAQ

What is the difference between docker compose up and start?

up creates containers (and networks/volumes) from your YAML, building or pulling as needed. start only restarts existing, stopped containers. Use up -d after editing the compose file; start to resume what you already created.

Should the file be named docker-compose.yml or compose.yaml?

Compose v2 prefers compose.yaml, but still reads the older docker-compose.yml. Either works — compose.yaml is the current convention.

How do services find each other?

By service name on the shared network Compose creates. In the example, WordPress connects to the database at host db — no IP addresses needed.

How do I safely change environment variables like passwords?

Edit the compose file, then docker compose up -d to recreate affected services. Note that a database's root password is only set on first creation; changing it later means updating it inside the running database, not just in the YAML.

Nxeon Docker hosting gives you a root-access VPS ready for Compose stacks on fast NVMe storage — spin up multi-container apps in minutes, with free migration help if you are moving one over.

#docker#docker compose#vps#containers#devops#seobatch

Deploy your first server in under a minute

Creating an account is free and takes no card details. You pay when you deploy — choose a billing term and pay from your wallet or by card at checkout.