Install Dolibarr with Docker Compose
A complete docker-compose.yml for Dolibarr 24 and MariaDB, tested end to end. With the official example, passwords are stored as MD5 with a cleartext copy, and scheduled jobs never run: how to avoid all three.
The official dolibarr/dolibarr image installs Dolibarr by itself on first start: one
docker-compose.yml file, one command, and a minute later the login screen shows up. This page
gives a complete setup, tested on 13 October 2026 with Dolibarr 24.0.1 and MariaDB 11.4.
It also fixes three flaws of the automatic install that the example in the official documentation does not mention, and that we saw on a fresh install:
- user passwords are stored as unsalted MD5, whereas a regular install protects them with
password_hash; - scheduled jobs never run: the cron container fails every five minutes with a key error that only shows in its logs;
- a cleartext copy of passwords stays in the database: every password changed from the interface is also stored in readable form, next to its encrypted version.
What you need
Docker with the Compose plugin: docker compose version must answer. The commands below are for
Linux and macOS. The whole install lives in one folder, for example ~/dolibarr: configuration,
database, documents and external modules.
mkdir -p ~/dolibarr/docker-init.d && cd ~/dolibarr
1. The docker-compose.yml file
services:
db:
image: mariadb:11.4
restart: unless-stopped
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
environment:
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MARIADB_DATABASE: dolibarr
MARIADB_USER: dolibarr
MARIADB_PASSWORD: ${DB_PASSWORD}
volumes:
- ./data/mariadb:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
web:
image: dolibarr/dolibarr:24.0.1
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment: &dolibarr-env
DOLI_DB_HOST: db
DOLI_DB_NAME: dolibarr
DOLI_DB_USER: dolibarr
DOLI_DB_PASSWORD: ${DB_PASSWORD}
DOLI_URL_ROOT: ${DOLI_URL_ROOT}
DOLI_ADMIN_LOGIN: ${ADMIN_LOGIN}
DOLI_ADMIN_PASSWORD: ${ADMIN_PASSWORD}
DOLI_INSTANCE_UNIQUE_ID: ${INSTANCE_UNIQUE_ID}
DOLI_COMPANY_NAME: ${COMPANY_NAME}
DOLI_COMPANY_COUNTRYCODE: GB
DOLI_ENABLE_MODULES: Cron
DOLI_CRON_KEY: ${CRON_KEY}
DOLI_CRON_USER: ${ADMIN_LOGIN}
PHP_INI_DATE_TIMEZONE: Europe/London
ports:
- "8080:80"
volumes:
- ./data/documents:/var/www/documents
- ./data/custom:/var/www/html/custom
- ./docker-init.d:/var/www/scripts/docker-init.d
cron:
image: dolibarr/dolibarr:24.0.1
restart: unless-stopped
depends_on:
- web
environment:
<<: *dolibarr-env
DOLI_CRON: 1
volumes:
- ./data/documents:/var/www/documents
- ./data/custom:/var/www/html/custom
Set DOLI_COMPANY_COUNTRYCODE to your two-letter country code (US, IE, BE…) and
PHP_INI_DATE_TIMEZONE to your time zone. The choices that matter:
- Exact versions, never
latest. The official example usesdolibarr/dolibarr:latestandmariadb:latest: a plaindocker compose pullcan then move you to a new major version without you deciding it. On 13 October 2026 the latest published image is 24.0.1; 24.0.2 is out, but its image is not on Docker Hub yet. - The MariaDB health check makes Dolibarr wait until the database is ready.
DOLI_ENABLE_MODULES: Cronenables the scheduled jobs module during the install, before the image stores the cron key. This is the fix for the second trap, explained below. You can add other modules, comma-separated (Societe,Facture,Cron).- The
cronservice uses the same image withDOLI_CRON: 1: it runs scheduled jobs every five minutes. It reuses every variable of thewebservice through&dolibarr-env. - Data stays in the
data/folder: database, documents, external modules. Together with.env, these are the only files to back up.
2. The .env file, with real passwords
Unless you change them, the official example keeps admin/admin for Dolibarr and root for the
database. This command creates the .env file with random values:
cat > .env <<EOF
DOLI_URL_ROOT=http://localhost:8080
ADMIN_LOGIN=admin
ADMIN_PASSWORD=$(openssl rand -hex 12)
DB_PASSWORD=$(openssl rand -hex 16)
DB_ROOT_PASSWORD=$(openssl rand -hex 16)
INSTANCE_UNIQUE_ID=$(openssl rand -hex 32)
CRON_KEY=$(openssl rand -hex 16)
COMPANY_NAME="My company"
EOF
chmod 600 .env
DOLI_URL_ROOT is the address you will open Dolibarr at. INSTANCE_UNIQUE_ID is used as an
encryption key for some settings: without it, the image writes a fixed value, the same for
everyone. Do not change it once installed, or those settings become unreadable.
3. The script that protects passwords
Create the file docker-init.d/password-hash.sql. The image runs the scripts in this folder once,
at the end of the install:
cat > docker-init.d/password-hash.sql <<'EOF'
INSERT INTO llx_const (name, value, type, visible, note, entity) VALUES ('MAIN_SECURITY_HASH_ALGO', 'password_hash', 'chaine', 0, '', 0);
INSERT INTO llx_const (name, value, type, visible, note, entity) VALUES ('DATABASE_PWD_ENCRYPTED', '1', 'chaine', 0, '', 0);
EOF
Do not put comments in it: the image removes lines starting with -- by rewriting the file.
The first line fixes trap 1 (MD5 passwords), the second one trap 3 (cleartext copy), both described below.
4. Start
docker compose up -d
docker compose logs -f web
Wait for the message “You can connect to the running Dolibarr web application” (about a minute in
our test), then leave the logs with Ctrl+C. Open http://localhost:8080 and log in with admin
and the password given by grep ADMIN_PASSWORD .env.
5. Right after the first login: change the admin password
The admin account is created before the step 3 script runs, so its password is still MD5.
Open Users & Groups, the admin record, Modify, and enter a new password. It is then
stored with password_hash, and Dolibarr asks you to log in again.
ADMIN_PASSWORD is only used on the very first start: changing it later in .env does nothing,
even after the containers are recreated. We checked.
Trap 1: MD5 passwords
When installing through the web wizard, Dolibarr stores MAIN_SECURITY_HASH_ALGO = password_hash
(file install/step5.php). The Docker image’s automatic install skips that step: the line meant to
do it is commented out in docker-run.sh. Without this setting, Dolibarr hashes every password
as unsalted MD5, including the ones you change later in the interface. On our fresh install, a
password changed from the user record was still 32 characters long in the database, the
signature of an MD5.
The matching issue in the image repository (#14) was closed in May 2026, but image 24.0.1, installed from scratch on 13 October 2026, still has the flaw.
To find out whether your instance is affected, open Admin Tools › About Security. If the line “Algorithm for Passwords” says “Undefined (If unset: ‘md5’)”, it is.
To fix an existing instance:
- logged in as administrator, open Setup › Other Setup and add the constant
MAIN_SECURITY_HASH_ALGOwith the valuepassword_hash; - without logging out, change your own password from your user record;
- have every other user change their password.
Dolibarr’s security page warns that after this change, all existing passwords become invalid. In 24.0.1 this is no longer true: we checked that a user still on MD5 can log in. But their password is only converted when they change it. Hence step 3.
Trap 2: a cron that never runs
With the official example setup (DOLI_CRON_KEY without DOLI_ENABLE_MODULES), the cron
container prints every five minutes:
Error: securitykey provided xxxxx... does not match securitykey in setup.
Nothing shows in Dolibarr: only docker compose logs cron reveals it. The cause: on first start,
the image writes your key by updating the CRON_KEY row in the database, but that row only exists
once the scheduled jobs module is enabled. The update touches no row and the key is never stored.
Enabling the module afterwards is not enough: the key stays empty.
On a new install, DOLI_ENABLE_MODULES: Cron solves it: the module is enabled before the key
is written. This is the setup on this page.
On an existing instance, enable the Scheduled jobs module, open its setup and paste the
CRON_KEY value from your .env file into “Security key for URL to launch cron jobs”. To check
right away:
docker compose exec cron /bin/su www-data -s /bin/sh -c "/var/www/scripts/cron/cron_run_jobs.php $(grep '^CRON_KEY=' .env | cut -d= -f2) admin"
The output must list the jobs with “Result of run_jobs OK”, not the key error.
Trap 3: a cleartext copy of passwords
When installing through the web wizard, Dolibarr also stores DATABASE_PWD_ENCRYPTED = 1 (same file
install/step5.php): this setting forbids it to keep a cleartext copy of passwords. The image’s automatic
install does not set it; docker-run.sh never mentions it. Without it, every password entered in the
interface is stored twice: encrypted in pass_crypted, and in cleartext in the pass column of the
llx_user table. Trap 1 does not change that: with password_hash, the encrypted version is right, but
the cleartext copy is still there.
On our fresh install with only the MAIN_SECURITY_HASH_ALGO line, the admin password changed at step 5
could be read as is in the database. It also showed in the HTML source of the user record opened for
editing. With the second line of the step 3 script, the column stays empty: we checked it on another
fresh install.
To find out whether your instance is affected, open Admin Tools › About Security. If the line “Encrypt passwords stored in database” says “No”, it is. This command lists the accounts whose password is in cleartext:
docker compose exec -T db sh -c 'mariadb -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE" -e "SELECT login FROM llx_user WHERE LENGTH(pass) > 0"'
To fix an existing instance:
- logged in as administrator, open Home › Setup › Security, Passwords tab, and click Activate next to “Encrypt passwords stored in database”. Dolibarr saves the setting and erases the cleartext copies, but only those shorter than 32 characters: it takes longer ones for old MD5 hashes and leaves them;
- erase the remaining ones:
docker compose exec -T db sh -c 'mariadb -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE" -e "UPDATE llx_user SET pass = NULL WHERE LENGTH(pass) > 0 AND LENGTH(pass_crypted) > 0"'
Login only uses the encrypted version: in our test, an account whose cleartext copy was erased still logs in. So there is no need to have passwords changed. Run the check command again: it must print nothing. An account still listed has no encrypted version, and the command spares it so as not to lock it out: have that user change their password.
What else the image does
conf.phpis rewritten every time the container is recreated, from the environment variables. ChangingDOLI_URL_ROOTin.envthen runningdocker compose up -dis enough to change the address, but any manual edit toconf.phpis lost.- Sessions are lost on every recreation: after an image update, everyone must log in again.
- File uploads are limited to 2 MB, even though PHP accepts 20 MB. The image does set PHP to
20 MB (its documentation wrongly says 2 MB), but Dolibarr applies its own limit, set to 2,048 KB
at install time. In our test, a 5 MB file is rejected with “File size is too large or file not
provided”. To raise it, open Home › Setup › Security, Files tab, and change “Maximum
size for uploaded files” (in KB:
20480for 20 MB). Above 20 MB, also addPHP_INI_UPLOAD_MAX_FILESIZEandPHP_INI_POST_MAX_SIZEto the variables. - The database service name must match
DOLI_DB_HOST. Without that variable, the image looks for a server namedmysql, and the web container waits forever.
Access from the Internet
Do not publish port 8080 as is. Put a proxy that handles HTTPS in front of Dolibarr (Caddy,
Traefik, Nginx), set the https:// address in DOLI_URL_ROOT, then restart with
docker compose up -d. If the proxy runs on the same machine, publish the port locally only
("127.0.0.1:8080:80") so nobody can bypass the proxy.
Backups
Backup from the interface works with this setup (Admin Tools › Backup): the file lands in
data/documents/admin/backup/. On the command line, the safest method is to export the database
from the MariaDB container, then copy the data/documents and data/custom folders along with the
.env file:
docker compose exec -T db sh -c 'mariadb-dump -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE"' > backup.sql
The file is complete if it ends with “– Dump completed”.
Upgrading later
Change the image version one major version at a time, never straight from 20 to 24: the image would skip migrations without telling you. The full procedure, tested, is in Upgrading Dolibarr in Docker.
If you would rather have it done for you, the Installation and setup package starts from this setup.