Skip to main content

Exercises and projects


Objectives

  • Consolidate your Portainer knowledge
  • Put the learned concepts into practice
  • Develop operational skills

Exercise 1: Installation and initial configuration

Objective

Install Portainer and perform the basic configuration.

Tasks

  1. Install Portainer CE on your local machine
  2. Create an administrator account
  3. Explore the dashboard
  4. Identify the existing resources (containers, images, volumes)

Starting commands

# Créer le volume de données
docker volume create portainer_data

# Déployer Portainer
docker run -d \
-p 8000:8000 \
-p 9443:9443 \
--name portainer \
--restart=always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer-ce:latest

Expected result


Exercise 2: Container management

Objective

Master the basic operations on containers.

Tasks

  1. Create an nginx container through the interface

    • Name: web-server
    • Image: nginx:alpine
    • Port: 8080:80
  2. Access the container logs

  3. Open a console and list the files in /usr/share/nginx/html

  4. Monitor the resource statistics

  5. Create a copy of the container with a different port (8081)

Expected result

  • Two working nginx containers
  • Ability to debug and monitor

Exercise 3: Deploying a stack

Objective

Deploy a multi-container application.

Tasks

  1. Create a new stack named blog

  2. Use the following compose:

version: "3.9"

services:
wordpress:
image: wordpress:latest
ports:
- "8000:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress123
WORDPRESS_DB_NAME: wordpress
depends_on:
- db

db:
image: mysql:8.0
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress123
MYSQL_ROOT_PASSWORD: rootpassword
volumes:
- db_data:/var/lib/mysql

volumes:
db_data:
  1. Deploy the stack

  2. Verify that both services are active

  3. Access WordPress at http://localhost:8000

Expected result

  • Working WordPress stack
  • Persistent volume for the database

Exercise 4: Volume management

Objective

Master storage management.

Tasks

  1. Create a volume named app-config

  2. Use the Browse function to:

    • Create a config.json file
    • Add configuration content to it
  3. Create a container that uses this volume:

services:
app:
image: alpine
command: sleep 3600
volumes:
- app-config:/config

volumes:
app-config:
external: true
  1. Verify that the file is accessible in the container

Expected result

  • Volume with a configuration file
  • Persistent data between restarts

Exercise 5: Network configuration

Objective

Configure custom networks to isolate services.

Tasks

  1. Create two networks:

    • frontend-net (172.20.0.0/16)
    • backend-net (172.21.0.0/16)
  2. Deploy a stack with isolation:

version: "3.9"

services:
nginx:
image: nginx:alpine
ports:
- "80:80"
networks:
- frontend-net
- backend-net

api:
image: nginx:alpine
networks:
- backend-net

database:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
networks:
- backend-net

networks:
frontend-net:
external: true
backend-net:
external: true
  1. Test the connectivity:
    • nginx can reach api and database
    • api can reach database
    • database cannot reach the outside

Expected result

  • Working network isolation
  • Services connected according to the architecture

Exercise 6: User management

Objective

Configure access control.

Tasks

  1. Create a developers team

  2. Create a dev1 user in this team

  3. Configure environment access:

    • developers: Operator (no deletion)
  4. Test by logging in as dev1:

    • Check the available permissions
    • Try to remove a container (must fail)

Expected result

  • Working RBAC
  • Permissions respected

Project 1: Development environment

Objective

Create a complete development environment managed through Portainer.

Architecture

┌─────────────────────────────────────────────────────────────────┐
│ Dev Environment │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Traefik │ │ App │ │ Postgres│ │ Redis │ │
│ │ :80 │──▶│ :3000 │──▶│ :5432 │ │ :6379 │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │
│ ┌──────▼──────────────▼──────┐ │
│ │ adminer mailhog │ │
│ │ :8080 :8025 │ │
│ └────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

Stack

version: "3.9"

services:
traefik:
image: traefik:v2.10
command:
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`traefik.localhost`)"
- "traefik.http.routers.dashboard.service=api@internal"

app:
image: node:18-alpine
command: sh -c "npm install && npm run dev"
working_dir: /app
volumes:
- ./app:/app
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`app.localhost`)"
- "traefik.http.services.app.loadbalancer.server.port=3000"
depends_on:
- postgres
- redis

postgres:
image: postgres:15
environment:
POSTGRES_USER: dev
POSTGRES_PASSWORD: devpassword
POSTGRES_DB: app_dev
volumes:
- postgres_data:/var/lib/postgresql/data

redis:
image: redis:7-alpine

adminer:
image: adminer
labels:
- "traefik.enable=true"
- "traefik.http.routers.adminer.rule=Host(`db.localhost`)"

mailhog:
image: mailhog/mailhog
labels:
- "traefik.enable=true"
- "traefik.http.routers.mailhog.rule=Host(`mail.localhost`)"
- "traefik.http.services.mailhog.loadbalancer.server.port=8025"

volumes:
postgres_data:

Success criteria

  • All services accessible via their subdomains
  • Working Traefik dashboard
  • Database accessible via Adminer
  • Emails intercepted by Mailhog

Project 2: Monitoring stack

Objective

Deploy a complete monitoring stack.

Architecture

┌─────────────────────────────────────────────────────────────────┐
│ Monitoring Stack │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Prometheus│───▶│ Grafana │ │ Alertmgr │ │
│ │ :9090 │ │ :3000 │ │ :9093 │ │
│ └─────┬─────┘ └───────────┘ └───────────┘ │
│ │ │
│ ┌────┼────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │cAdvisor│ │ Node │ │ Pushgw │ │
│ │ :8080 │ │Exporter│ │ :9091 │ │
│ └────────┘ └────────┘ └────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

Stack

version: "3.9"

services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.enable-lifecycle'

grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin123
volumes:
- grafana_data:/var/lib/grafana

cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro

node-exporter:
image: prom/node-exporter:latest
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'

alertmanager:
image: prom/alertmanager:latest
ports:
- "9093:9093"
volumes:
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml

volumes:
prometheus_data:
grafana_data:

Success criteria

  • Prometheus collects the metrics
  • Grafana displays the dashboards
  • cAdvisor monitors the containers
  • Alertmanager configured

Project 3: Multi-team environment

Objective

Configure Portainer to manage multiple teams with isolation.

Tasks

  1. Create 3 teams:

    • frontend-team
    • backend-team
    • devops-team
  2. Create users for each team

  3. Create dedicated stacks:

    • frontend-app (accessible to frontend-team)
    • backend-api (accessible to backend-team)
    • infrastructure (accessible to devops-team)
  4. Configure the permissions:

    • Each team only accesses its resources
    • devops-team has read-only access to everything

Success criteria

  • Complete isolation between teams
  • devops-team can monitor without modifying

Validation quiz

Questions

  1. Which port does Portainer use for HTTPS by default?

    • a) 8080
    • b) 9000
    • c) 9443
    • d) 443
  2. How do you access a container's console?

    • a) Via SSH
    • b) Via the Console tab in Portainer
    • c) Via Telnet
    • d) It is not possible
  3. What is the difference between Agent and Edge Agent?

    • a) None
    • b) Edge Agent initiates the outbound connection
    • c) Agent is for Kubernetes
    • d) Edge Agent is faster
  4. How do you deploy a multi-container application?

    • a) Create each container one by one
    • b) Use a Stack
    • c) Use only the CLI
    • d) It is not possible
  5. What does the Browse function on a volume allow?

    • a) View the logs
    • b) Navigate and modify the files
    • c) Remove the volume
    • d) Mount the volume

Answers

  1. c) 9443 - Default HTTPS port
  2. b) Via the Console tab - Exec into the container
  3. b) Edge Agent initiates the outbound connection - For environments behind NAT
  4. b) Use a Stack - Docker Compose in Portainer
  5. b) Navigate and modify the files - Integrated file browser

Additional resources

Official documentation

Community


Conclusion

Congratulations! You have completed the Portainer course.

You now master:

  • Installation and configuration
  • Managing containers, images, volumes, networks
  • Deploying stacks
  • User management and RBAC
  • Production best practices

Portainer is a powerful tool that considerably simplifies container management. Keep practicing and exploring the advanced features!


← Best practices | Table of contents