Docker in practice: the commands and best practices we use to run GLPI

How NexTool runs and supports GLPI on Docker for clients: architecture with separate volumes, a production docker-compose, the commands we actually use in support and the best practices (pinned tag, non-root, secrets and volume backup).

Docker stopped being a trend and became the standard way we deliver and support GLPI for clients. Instead of installing PHP, MariaDB and a web server straight onto the operating system, we package GLPI and its dependencies into versioned containers - which makes the install reproducible, the rollback trivial and maintenance predictable. This guide gathers the commands we actually use day to day in support and the practices we apply to run the service desk in production safely.

How we run GLPI on Docker

The architecture we standardize is deliberately lean: one container for the GLPI application (PHP-FPM and web server), one for the MariaDB database and, when the ticket volume warrants it, a Redis for session and cache. Everything orchestrated by a single docker-compose.yml, behind a reverse proxy with TLS. The data that must survive any container recreation - database, uploaded files and configuration - lives in named volumes, never inside the container. That separation between code (the image, disposable) and state (the volume, precious) is what turns a GLPI version upgrade into a matter of minutes rather than an all-nighter.

Artifact: the GLPI stack we provision

This is the skeleton of the compose file we bring up on every deployment, already with separate volumes and no plaintext credential inside the file:

# docker-compose.yml - production GLPI stack
services:
  glpi:
    image: glpi/glpi:11.0        # pinned tag, never 'latest'
    restart: unless-stopped
    depends_on:
      - glpidb
    volumes:
      - glpi_files:/var/glpi     # uploaded files and GLPI configuration
    networks: [ edge, internal ]

  glpidb:
    image: mariadb:11.4
    restart: unless-stopped
    environment:
      MARIADB_DATABASE: glpi
      MARIADB_USER: glpi
      MARIADB_PASSWORD_FILE: /run/secrets/db_password   # password via secret, never plaintext
    volumes:
      - glpi_db:/var/lib/mysql   # the database lives here, outside the container
    networks: [ internal ]
    secrets: [ db_password ]

volumes:
  glpi_files:
  glpi_db:

networks:
  edge:      # exposed to the reverse proxy
  internal:  # private database, no external route
    internal: true

secrets:
  db_password:
    file: ./secrets/db_password.txt   # file 600, outside git

The commands we actually use in support

Day to day, a handful of commands solve most interventions. These are the ones the team types almost without thinking:

CommandWhen we use it supporting GLPI
docker compose up -dBrings up or updates the stack after a change to the compose or the image tag
docker compose logs -f glpiFollows a ticket error in real time when the client reports a failure
docker compose exec glpi bashEnters the container to run php bin/console (cron, cache, migration)
docker compose psChecks that application and database are up before opening an incident
docker statsDiagnoses CPU and memory spikes when the complaint is slowness
docker compose down (without -v)Recreates the stack keeping the volumes; the -v would wipe the database

The common - and costly - mistake we have seen in careless operators is running docker compose down -v thinking it only restarts the containers. The -v flag removes the volumes, and with them the entire GLPI database. In support we treat -v as a destructive command: it appears in no operating procedure, and every volume backup is validated before any maintenance that comes near it.

Best practices we apply in production

  • Pinned tag, never latest - we pin the image version (e.g. glpi/glpi:11.0, mariadb:11.4) so a pull does not drag in an unexpected version in the middle of maintenance.
  • Non-root and least privilege - the application does not run as root, and the database sits on the internal network, with no external route, reachable only by the GLPI container.
  • Secrets out of the compose - the database password comes in via a Docker secret or a read-only mounted file, never in environment: as plaintext committed to git.
  • The volume is sacred - database, files and configuration in named volumes, with a daily logical database dump tested with a real restore (a backup that has never been restored is not a backup).
  • Lean, scanned images - a slim base and scanning with Trivy or Docker Scout so we do not carry a known CVE into production.

A detail only someone who supports GLPI in a container knows: upgrading the GLPI version is not just swapping the image tag. After docker compose up -d with the new image, you must run the database migration from the console inside the container, otherwise the application comes up pointing at an old schema and returns a 500 error on the first screen. That is why our upgrade procedure always chains the same order: backup the database volume, swap the tag, up -d, migrate and only then release access to users. When the client uses the NexTool plugin, that same bring-up reloads the ecosystem modules - so we also validate the health of the integrations (webhook queues, sync) before calling the environment stable.

If your GLPI operation still runs installed straight on the server, with no separate volumes and no tested backup, NexTool deploys and supports that environment on Docker the right way - with a versioned stack, predictable upgrades and observability next to the service desk. Talk to us about GLPI support on containers.


This content was produced with the aid of artificial intelligence and reviewed by the Nextool Solutions team.

Frequently Asked Questions

Because the containerized stack is reproducible and the state (database, files, configuration) is isolated in volumes. That makes version upgrades predictable, rollback trivial and maintenance a matter of minutes, rather than a manual install that is hard to reproduce on another environment.

Day to day we use docker compose up -d (bring up/update), docker compose logs -f glpi (follow errors in real time), docker compose exec glpi bash (run the GLPI console), docker compose ps (check app and database are up) and docker stats (diagnose CPU and memory spikes).

docker compose down on its own stops and removes the containers but preserves the named volumes - the database stays intact. What wipes the data is the -v flag (docker compose down -v), which also removes the volumes. We treat -v as a destructive command and it never enters routine procedure.

What needs backing up is the state, not the image. We take a daily logical database dump (from the MariaDB container) and a copy of the uploaded-files and configuration volume. Above all, we test the restore: a backup that has never been restored does not count as a backup.

Order matters: first back up the database volume, then swap the image tag, docker compose up -d, run the database migration from the console inside the container and only then release access. Skipping the migration makes the application come up with an old schema and return a 500 error.

Need help?