Repository navigation
Docker Compose
For local evaluation or a lightweight self-hosted deployment. This is not the same tooling as Bare Metal Install — that's the hardened, TLS-terminating, systemd-integrated path recommended for a production instance you're running long-term. This is the "git clone && docker compose up" path: fast to try, deliberately dumb about hardening (no fail2ban/ufw equivalent — a container has none of those concepts), and expects you to put a real reverse proxy with TLS in front of it for anything beyond localhost.
git clone https://github.com/TheTractorHacker/itflow.git
cd itflow
cp .env.example .env # edit DB_PASSWORD/DB_ROOT_PASSWORD, and DOCKER_UID/DOCKER_GID (run `id -u`/`id -g`)
docker compose up -d --buildYou don't need to build anything separately first — docker compose up builds the app image
automatically the first time (it's defined with a build: block in docker-compose.yml), and pulls
the public mariadb:11 image for the database. Use --build again any time you edit docker/Dockerfile
or the other docker/* files, since Compose won't notice those changes on its own otherwise.
Then visit http://localhost:8080/ (or whatever APP_PORT you set in .env) — it redirects straight
to the same browser-based /setup/ wizard a manual install would use. When it asks for a database
host, enter db and the credentials from your .env.
First boot can take a few minutes, not seconds — it runs composer install inside the container
(since vendor/ isn't in git) and imports the full db.sql schema, and both were noticeably slower
inside a container than on bare metal in testing, depending on your Docker storage driver. That's a
one-time cost per fresh volume, not a hang.
nginx + PHP-FPM 8.4 + MariaDB + Redis, with the app code bind-mounted from your checkout — not baked into the image. That means:
-
config.php,uploads/,backups/, andvendor/all live on your host, in the same directory you randocker compose upfrom. Stopping, removing, or rebuilding the container doesn't touch them. - The database itself lives in a Docker-managed named volume (
itflow_db_data). It survivesdocker compose down, butdocker compose down -vdeletes it — don't use-vunless you mean to start over. -
git pull+docker compose up -d --buildis the update path — the same model as Updating and Hardening'sdeploy/update.sh, just without the wrapper script. - Redis runs inside the same container on
127.0.0.1:6380, matching whatincludes/redis_functions.phpexpects, so live ticket/chat push works the same as bare metal.
Rather than going through the setup wizard, you can have the first boot restore a Backup and Restore archive automatically:
- Put the backup file and its passphrase file under
./restore/in your checkout (this directory is always bind-mounted read-only into the container at/var/www/restore, harmless if empty). - In
.env, set:RESTORE_FROM=/var/www/restore/backup-itflow-20260101T000000Z.tar.gz.enc RESTORE_PASSPHRASE_FILE=/var/www/restore/passphrase -
docker compose up -d --build.
On first boot (only when config.php doesn't exist yet), the entrypoint writes a throwaway placeholder
config.php and schema via scripts/setup_cli.php, then immediately hands off to deploy/restore.sh
to overwrite it with your real backup — the same mechanism Bare Metal Install's --restore-from
flag uses. Verified end-to-end in testing: database replaced, uploads/ replaced, and (if the backup's
manifest has one) config_settings_enc_key re-applied so encrypted settings still decrypt correctly.
If RESTORE_PASSPHRASE_FILE is missing or the restore fails, the entrypoint logs why and falls back to
leaving config.php absent, so the browser /setup wizard is still reachable to retry or do a fresh
install instead.