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:
| Profile | Active users | RAM | vCPU | PHP model |
|---|---|---|---|---|
| Pilot / POC | up to 20 | 2 GB | 2 | mod_php is enough |
| Small | up to 100 | 4 GB | 2 | PHP-FPM + OPcache |
| Medium | 100 to 400 | 8 GB | 4 | Dedicated PHP-FPM, SSD disk |
| Large | 400+ | 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
- Remove the installer:
rm /var/www/glpi/install/install.php. Careful: every upgrade recreates this file - remove it again after each update. - Change the passwords of the four default users:
glpi,tech,normalandpost-only. - Configure SMTP under Setup > Notifications and send a test email.
- 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.