Found it useful? Don't miss the next lab
Create a dedicated working directory (~/docker/portainer) to hold the stack's compose.yaml. Keeping each stack in its own folder makes it easy to version, back up and manage with `docker compose` from there.
mkdir -p ~/docker/portainer
Write compose.yaml following the current Compose Specification: no `version:` key, image pinned to `portainer-ce:2.40.0` (never `:latest`), ports 9443 (HTTPS UI) and 8000 (Edge Agent tunnel), the Docker socket mounted read-only (`:ro`) since Portainer drives everything over the API, and a named `portainer_data` volume so the data survives upgrades.
cat > ~/docker/portainer/compose.yaml <<'YAML'
services:
portainer:
image: portainer/portainer-ce:2.40.0
container_name: portainer
restart: unless-stopped
security_opt:
- no-new-privileges:true
ports:
- "9443:9443"
- "8000:8000"
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- portainer_data:/data
volumes:
portainer_data:
YAML
Validate the syntax before starting anything: `docker compose config` parses the file and fails on a bad indent or an invalid key. If it prints `compose.yaml OK`, you're good.
docker compose -f ~/docker/portainer/compose.yaml config -q && echo 'compose.yaml OK'
Bring the stack up in the background (`-d`). Docker pulls the image if missing, creates the `portainer_data` volume and starts the `portainer` container with `restart: unless-stopped`, which brings it back after a host reboot.
cd ~/docker/portainer && docker compose up -d
Check the service is `running` and listening on `0.0.0.0:9443` before opening the browser. `docker compose ps` reports the status of the stack defined in this directory.
cd ~/docker/portainer && docker compose ps
Open https://<IP>:9443 (self-signed cert: accept the warning), create the admin user with a 12+ character password —the form expires after 5 minutes— and log in: the local Docker environment is right there and the install works.
Was this lab useful? Rate it:
Cheat sheet
-
mkdir -p ~/docker/portainer -
cat > ~/docker/portainer/compose.yaml <<'YAML' services: portainer: image: portainer/portainer-ce:2.40.0 container_name: portainer restart: unless-stopped security_opt: - no-new-privileges:true ports: - "9443:9443" - "8000:8000" volumes: - /etc/localtime:/etc/localtime:ro - /var/run/docker.sock:/var/run/docker.sock:ro - portainer_data:/data volumes: portainer_data: YAML -
cd ~/docker/portainer && docker compose up -d