How to Install GLPI 11 on Linux (Debian/Ubuntu): Step by Step

A real step-by-step install of GLPI 11 on Debian 12 or Ubuntu 24.04: PHP and the two php.ini files that matter, MariaDB with time zones, downstream.php to move config and files out of the webroot, CLI cron and a post-install checklist.

Installing GLPI 11 on Linux is smooth right up to the line where you move config/ and files/ out of the webroot and the installer suddenly "can't find" the database. This is the step by step we use when provisioning GLPI for clients: the real commands, the two php.ini files that actually matter, the file that prevents the most common manual-install mistake, and the post-install checklist that locks the environment down.

Before you start: size the server

GLPI 11 runs on the classic LAMP stack: Apache (or Nginx) + PHP 8.1 or newer + MariaDB 10.5+ (or MySQL 8). On Debian 12 the default PHP is 8.2; on Ubuntu 24.04 LTS it is 8.3 - both work. What really changes between a pilot and an environment with hundreds of technicians is RAM and the PHP execution model (mod_php vs PHP-FPM). This is the matrix we use to size it:

ProfileActive usersRAMvCPUPHP model
Pilot / POCup to 202 GB2mod_php is enough
Smallup to 1004 GB2PHP-FPM + OPcache
Medium100 to 4008 GB4Dedicated PHP-FPM, SSD disk
Large400+16 GB+4+Database on a separate host

1. System, web server, PHP and extensions

Update the system and install everything at once. The extensions are not optional: GLPI checks each one on the requirements screen.

apt update && apt upgrade -y
apt install -y apache2 mariadb-server \
  php php-{mysql,curl,gd,intl,xml,mbstring,zip,bz2,ldap,imap,apcu} \
  libapache2-mod-php certbot python3-certbot-apache

The common mistake here is forgetting php-intl: the package install still succeeds, but GLPI blocks on the requirements check and date formatting and internationalization break. php-ldap and php-imap are only needed if you integrate Active Directory and the mail receiver, but installing them now saves a reinstall later.

2. The two php.ini files that matter

This is what separates an install that "works" from one that also works at 3 a.m. PHP keeps separate settings per SAPI: Apache reads /etc/php/8.2/apache2/php.ini, but the GLPI cron runs from the command line and reads /etc/php/8.2/cli/php.ini. Set both to the same values:

memory_limit = 256M
upload_max_filesize = 20M
post_max_size = 20M
max_execution_time = 300
max_input_vars = 5000
session.cookie_httponly = On
date.timezone = UTC
; remember: this same file also exists at .../cli/php.ini

In our support work, the number one cause of "the automatic actions stopped" is almost never the system cron: it is the 128M memory_limit in the CLI php.ini choking inventory sync or batch notifications, while the Apache one was already at 256M and nobody suspected it. Edit both files.

3. MariaDB, the database and the time zone

Run mysql_secure_installation and create the database with utf8mb4 (accents and emojis depend on it):

mysql -u root -p
CREATE DATABASE glpi CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'glpi'@'localhost' IDENTIFIED BY 'StrongPassword';
GRANT ALL PRIVILEGES ON glpi.* TO 'glpi'@'localhost';
FLUSH PRIVILEGES;

One detail that only shows up later: the GLPI time zone selector stays empty until you import the MariaDB time zone tables and grant read access to the GLPI user. Do it now:

# Load the time zone tables; without this the timezone selector stays empty
mariadb-tzinfo-to-sql /usr/share/zoneinfo | mysql -u root mysql
mysql -u root -e "GRANT SELECT ON mysql.time_zone_name TO 'glpi'@'localhost'; FLUSH PRIVILEGES;"

4. Move config, files and logs out of the webroot - and tell GLPI where they went

Download GLPI 11 and extract it:

cd /tmp
wget https://github.com/glpi-project/glpi/releases/download/11.0.0/glpi-11.0.0.tgz
tar -xzf glpi-11.0.0.tgz -C /var/www/
chown -R www-data:www-data /var/www/glpi

For security, sensitive data must not live under the webroot. Move it:

mkdir -p /etc/glpi /var/lib/glpi/files /var/log/glpi
mv /var/www/glpi/config/* /etc/glpi/
mv /var/www/glpi/files/*  /var/lib/glpi/files/
chown -R www-data:www-data /etc/glpi /var/lib/glpi /var/log/glpi

Here is the most common manual-install mistake: moving the directories and never telling GLPI where they went. The result is the installer recreating config/ and files/ inside the webroot, you locking down permissions in the wrong place, and the environment staying exposed. The correct way is to create inc/downstream.php - it is read by both Apache and the CLI, so it fixes web and cron in one shot:

<?php // /var/www/glpi/inc/downstream.php
define('GLPI_CONFIG_DIR', '/etc/glpi');
define('GLPI_VAR_DIR',    '/var/lib/glpi/files');
define('GLPI_LOG_DIR',    '/var/log/glpi');

5. Virtual host, HTTPS and the DocumentRoot on public/

The DocumentRoot points to /var/www/glpi/public, never to the install root - this is mandatory since GLPI 10 and it is what keeps config and files from being served over the web.

<VirtualHost *:80>
    ServerName glpi.cliente.com.br
    DocumentRoot /var/www/glpi/public   # never /var/www/glpi

    <Directory /var/www/glpi/public>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
a2ensite glpi.conf && a2enmod rewrite
a2dissite 000-default.conf
systemctl reload apache2
certbot --apache -d glpi.cliente.com.br

6. Install from the command line and set up cron

With downstream.php in place, db:install already writes config_db.php to /etc/glpi - no need to export any variable:

php /var/www/glpi/bin/console db:install \
  --db-host=localhost --db-name=glpi \
  --db-user=glpi --db-password='StrongPassword' \
  --default-language=en_GB --no-interaction

GLPI processes notifications, inventory and automatic actions through an external cron. Schedule it every minute - GLPI itself decides what runs on each tick, based on each task's frequency:

echo "* * * * * www-data /usr/bin/php /var/www/glpi/front/cron.php &>/dev/null" > /etc/cron.d/glpi

Then, under Setup > Automatic actions, switch the run mode to CLI. If it stays on internal mode (triggered on user page loads), the tasks vanish overnight and on weekends - exactly when you want inventory and backups running.

7. Post-install: the checklist that locks it down

  1. Remove the installer: rm /var/www/glpi/install/install.php. Careful: every upgrade recreates this file - remove it again after each update.
  2. Change the passwords of the four default users: glpi, tech, normal and post-only.
  3. Configure SMTP under Setup > Notifications and send a test email.
  4. Confirm the installer is gone and the cron has run:
# Is the installer still reachable? (expected: 404)
curl -s -o /dev/null -w "%{http_code}\n" https://glpi.cliente.com.br/install/install.php
-- Is the external cron running? (lastrun should be recent)
SELECT name, state, lastrun FROM glpi_crontasks ORDER BY lastrun DESC LIMIT 5;

What we check when we take over someone else's GLPI

When we take over support of an environment installed by another team, the diagnosis is always the same trio: does inc/downstream.php exist, or are there two config directories coexisting (one live, one ghost that someone edits with no effect)? Does the CLI memory_limit match Apache's? And is the automatic-actions mode set to CLI or tied to user page loads? Those three points explain most of the "GLPI is slow" and "notifications don't arrive" tickets we get - and none of them show up in an install tested only by clicking around the UI during business hours.

Need a GLPI 11 provisioned and supported with this rigor, without inheriting installation debt? Learn about NexTool's GLPI support and maintenance.


Reviewed by the NexTool Solutions team.

Frequently Asked Questions

PHP 8.1 or newer. Debian 12 ships 8.2 and Ubuntu 24.04 LTS ships 8.3, both supported. Required extensions include intl, mysqli, curl, gd, mbstring, xml and zip; ldap and imap are only needed for Active Directory and the mail receiver.

Yes. Apache reads the apache2 SAPI php.ini, but front/cron.php runs under CLI and reads the CLI php.ini. Editing only one leaves half of GLPI (automatic actions, batch inventory, notifications) with the wrong memory limit and timeout.

It is where you redefine GLPI_CONFIG_DIR, GLPI_VAR_DIR and GLPI_LOG_DIR to move config, files and logs out of the webroot. Because it is read by both the web and the CLI, db:install and cron see the same paths. Without it, moving the directories breaks the install.

GLPI reads time zones from MariaDB's time_zone tables, which are not populated by default. Run mariadb-tzinfo-to-sql to import them and grant SELECT on mysql.time_zone_name to the GLPI user.

For up to about 100 users, mod_php with OPcache is fine. Above that, PHP-FPM isolates the workers, improves memory usage and lets you tune concurrency independently of Apache. It is the default choice in larger environments.

In production, yes. Without TLS, credentials travel in plain text over the network. Use Let's Encrypt (Certbot). Behind a reverse proxy, make sure the session cookie has the Secure flag and trust the X-Forwarded-Proto header.

Need help?