Production stack
Requirements
Section titled “Requirements”- A Linux host with Docker Engine and the Docker Compose plugin.
- Ports 80 and 443 reachable by your users (or any other port, set in
.env). - For Let’s Encrypt certificates: a DNS name pointing to the host, and ports 80 and 443 reachable from the Internet.
What gets installed
Section titled “What gets installed”Auditix is published as three images (frontend, backend, updater) on the Auditix registry,
rg.fr-par.scw.cloud/auditix, always deployed with the same tag. The compose file adds the services
they rely on:
| Service | Role |
|---|---|
gateway |
The only published service: serves Auditix on HTTP and HTTPS. |
frontend |
The web interface. |
backend |
The application. Applies database changes itself when it starts. |
updater |
Applies the updates started from Settings › Platform › Update. Optional. |
orchestrator |
Starts and stops the worker containers that run the background work. |
developer |
The reference of the external API, at /developer. |
logs |
Forwards the logs to the syslog servers of Settings › Platform › Logs. |
rabbitmq |
Queues of the background work. |
mercure |
Real-time updates of the interface. |
database |
PostgreSQL: every piece of data lives here. |
The updater and orchestrator services have access to Docker. They only ever act on the containers
of their own installation (the compose project deployed from the same folder), never on other
containers of the host.
Install
Section titled “Install”-
Log in to the registry (skip this step if the registry is public), with a Scaleway API secret key allowed to read the registry:
Terminal window docker login rg.fr-par.scw.cloud/auditix -u nologin# Password: your Scaleway API secret key -
Create the installation folder:
Terminal window mkdir -p /opt/auditix && cd /opt/auditix -
Create
compose.yamlwith the content below. -
Create
.env, readable by root only (chmod 600 .env), and fill each secret once withopenssl rand -hex 32:Terminal window # "latest": updated from Settings › Platform › Update. Or a fixed release (vX.Y.Z).AUDITIX_VERSION=latestHTTP_PORT=80HTTPS_PORT=443# Generate each once with: openssl rand -hex 32APP_SECRET=POSTGRES_PASSWORD=MERCURE_JWT_SECRET=RABBITMQ_PASSWORD=GATEWAY_SECRET=UPDATER_SECRET= -
Start:
Terminal window docker compose pulldocker compose up -ddocker compose ps # every service should be "healthy" -
Sign in at
http://<host>with the default administrator,admin/password. Auditix asks for a new password before anything else: do it right away, before exposing the service.
compose.yaml
Section titled “compose.yaml”name: auditix
# Every service hands its output to Docker's syslog driver, towards the log collector, which# forwards it to the syslog servers of Settings › Platform › Logs.x-logging: &logging driver: syslog options: syslog-address: udp://127.0.0.1:15013 syslog-format: rfc5424 tag: "{{.Name}}"
services: # The only published service. HTTPS and the certificate are set in Settings › Platform › Web # access; the gateway reads them from the backend. gateway: logging: *logging image: traefik:v3.7 command: - --entrypoints.web.address=:80 - --entrypoints.websecure.address=:443 - --entrypoints.web.forwardedHeaders.trustedIPs=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1/32 - --entrypoints.websecure.forwardedHeaders.trustedIPs=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1/32 - --providers.http.endpoint=http://backend:8080/internal/gateway - --providers.http.pollInterval=5s - --providers.http.headers.X-Gateway-Secret=${GATEWAY_SECRET:?Set GATEWAY_SECRET in .env} - --certificatesresolvers.letsencrypt.acme.storage=/data/acme.json - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web - --certificatesresolvers.letsencrypt-staging.acme.storage=/data/acme-staging.json - --certificatesresolvers.letsencrypt-staging.acme.httpchallenge.entrypoint=web - --certificatesresolvers.letsencrypt-staging.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory - --ping=true - --global.checkNewVersion=false - --global.sendAnonymousUsage=false ports: - "${HTTP_PORT:-80}:80" - "${HTTPS_PORT:-443}:443" volumes: - gateway:/data healthcheck: test: ["CMD", "traefik", "healthcheck", "--ping"] interval: 10s timeout: 3s retries: 6 depends_on: backend: condition: service_healthy restart: unless-stopped
frontend: logging: *logging image: rg.fr-par.scw.cloud/auditix/frontend:${AUDITIX_VERSION:-latest} environment: BACKEND_URL: http://backend:8080 depends_on: backend: condition: service_healthy restart: unless-stopped
backend: logging: *logging image: rg.fr-par.scw.cloud/auditix/backend:${AUDITIX_VERSION:-latest} environment: &backend-environment APP_SECRET: ${APP_SECRET:?Set APP_SECRET in .env} DATABASE_URL: postgresql://auditix:${POSTGRES_PASSWORD}@database:5432/auditix?serverVersion=18&charset=utf8 MERCURE_URL: http://mercure/.well-known/mercure MERCURE_JWT_SECRET: ${MERCURE_JWT_SECRET:?Set MERCURE_JWT_SECRET in .env} MESSENGER_TRANSPORT_DSN: amqp://auditix:${RABBITMQ_PASSWORD:?Set RABBITMQ_PASSWORD in .env}@rabbitmq:5672/%2f GATEWAY_SECRET: ${GATEWAY_SECRET} UPDATER_SECRET: ${UPDATER_SECRET:-} LOGS_COLLECTOR_HOST: logs # Never published: the internal API is only for the frontend; integrations use the # external API (/api/v1) through the gateway. volumes: - logs-config:/app/var/logs-collector depends_on: database: condition: service_healthy rabbitmq: condition: service_healthy restart: unless-stopped
# Applies the updates started from Settings › Platform › Update. The only service with access to # Docker: it only acts on the services of this file (its own compose project), never on other # containers of the host. Optional: without it, update by hand (see Update below). updater: logging: *logging image: rg.fr-par.scw.cloud/auditix/updater:${AUDITIX_VERSION:-latest} environment: BACKEND_URL: http://backend:8080 UPDATER_SECRET: ${UPDATER_SECRET:?Set UPDATER_SECRET in .env} volumes: - /var/run/docker.sock:/var/run/docker.sock # The installation directory: the backups taken before updates in backups/, and its .env # when AUDITIX_VERSION is a fixed release. - .:/install depends_on: backend: condition: service_healthy restart: unless-stopped
# Runs the background work: starts and stops the worker containers (the backend image, started # with the "worker" command) as the pools of Settings › Platform › Workers ask, a fixed number or # following the backlog of their queues. Same image as the updater; it only acts on the worker # containers it creates for this installation. Without it, no worker runs (see Scale the workers). orchestrator: logging: *logging image: rg.fr-par.scw.cloud/auditix/updater:${AUDITIX_VERSION:-latest} entrypoint: ["auditix-orchestrator"] environment: BACKEND_URL: http://backend:8080 UPDATER_SECRET: ${UPDATER_SECRET:?Set UPDATER_SECRET in .env} volumes: - /var/run/docker.sock:/var/run/docker.sock depends_on: backend: condition: service_healthy restart: unless-stopped
# The developer portal: the reference of the external API, at /developer. developer: logging: *logging image: scalarapi/api-reference:0.6.12 environment: BASE_PATH: /developer/scalar API_REFERENCE_CONFIG: '{"sources":[{"url":"/api/v1/openapi.json","title":"Auditix API"}],"theme":"none","withDefaultFonts":false,"customCss":"@import url(\"/theme/developer\");","favicon":"/icon.svg","metaData":{"title":"Auditix API"},"defaultHttpClient":{"targetKey":"shell","clientKey":"curl"},"showDeveloperTools":"never","agent":{"disabled":true},"mcp":{"disabled":true}}' restart: unless-stopped
# The log collector: forwards the logs to the syslog servers of the settings. logs: image: timberio/vector:0.59.0-alpine command: ["--config-dir", "/etc/vector/auditix", "--watch-config"] ports: - "127.0.0.1:15013:5514/udp" volumes: - logs-config:/etc/vector/auditix:ro depends_on: backend: condition: service_healthy restart: unless-stopped
# Queues of the background work. rabbitmq: logging: *logging image: rabbitmq:4.3-management-alpine hostname: rabbitmq environment: RABBITMQ_DEFAULT_USER: auditix RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD} volumes: - rabbitmq:/var/lib/rabbitmq healthcheck: test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"] interval: 10s timeout: 10s retries: 12 restart: unless-stopped
# Real-time updates. Not published: the browsers reach it through the gateway. mercure: logging: *logging image: dunglas/mercure:v1.1 environment: SERVER_NAME: ":80" MERCURE_TRUSTED_ISSUERS: auditix MERCURE_PUBLISHER_JWT_KEY: ${MERCURE_JWT_SECRET} MERCURE_PUBLISHER_JWT_ALG: HS256 MERCURE_SUBSCRIBER_JWT_KEY: ${MERCURE_JWT_SECRET} MERCURE_SUBSCRIBER_JWT_ALG: HS256 MERCURE_EXTRA_DIRECTIVES: | resource_identifier http://mercure/.well-known/mercure cookie_name auditix_live transport bolt { path /data/mercure.db size 1000 cleanup_frequency 0.3 } heartbeat 15s volumes: - mercure:/data restart: unless-stopped
database: logging: *logging image: postgres:18-alpine environment: POSTGRES_DB: auditix POSTGRES_USER: auditix POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env} volumes: - database:/var/lib/postgresql healthcheck: test: ["CMD", "pg_isready", "-U", "auditix", "-d", "auditix"] interval: 5s timeout: 3s retries: 10 restart: unless-stopped
volumes: database: gateway: logs-config: mercure: rabbitmq:First steps after installing
Section titled “First steps after installing”- In Settings › Platform › General, set the Instance name, the Time zone, the Default language and, in Appearance, the Default theme and Main colour (see Settings).
- Activate the licence.
- Set up HTTPS.
- Create the tenants and the accounts.
Accounts can also be created from the command line (with Portainer, from the console of the backend container):
docker compose exec backend bin/console auditix:user:create jane@example.com \ --first-name=Jane --last-name=Doe --platform-admin --tenant="My company"With Portainer
Section titled “With Portainer”- Stacks › Add stack › Web editor: paste
compose.yaml. - Put the variables of
.envin the environment variables of the stack, withAUDITIX_VERSION=latest. - In the
updaterservice, remove the.:/installvolume: Portainer keeps the files of a stack in its own volume. The backups taken before updates then go to a Docker volume named<stack>_backups, created by the updater.
Nothing else is needed: updates never change the stack, and the workers are started by the
orchestrator service, outside the stack.
Keep the installation folder (/opt/auditix): the updater mounts it on /install to store the
backups it takes before updates in backups/, and to change .env when AUDITIX_VERSION is a fixed
release.
In Settings › Platform › Web access, give the Host name of the instance (its DNS must point to the server), then choose the Certificate:
- Let’s Encrypt: free, issued and renewed automatically. Ports 80 and 443 must be reachable from the Internet. Let’s Encrypt (test) checks the setup first, with certificates browsers do not trust, without hitting the limits of Let’s Encrypt.
- My own certificate: paste the Certificate (PEM) (with its intermediate certificates) and the Private key (PEM). The key is stored encrypted and never shown again.
- No HTTPS (HTTP only): for a test, or behind a reverse proxy that handles HTTPS.
Then switch on Redirect HTTP to HTTPS and, once HTTPS works, Strict HTTPS (HSTS). Changes apply within a few seconds, without restart. If a wrong setting makes Auditix unreachable over HTTPS, switch HTTPS off from the server:
docker compose exec backend bin/console auditix:web:resetBehind a reverse proxy that already handles HTTPS, leave HTTPS off and forward to the HTTP port of the
gateway with the X-Forwarded-Proto and X-Forwarded-For headers.
Background work
Section titled “Background work”The orchestrator service starts the worker containers that do the background work (scheduled
tasks, checks, imports…), as set in Settings › Platform › Workers › Pools: a fixed number of
containers, or Automatic scaling with the number of messages waiting. The default pool runs 2 to 4
containers. Without the orchestrator, the backend still runs the scheduled tasks every five minutes, but
nothing else is processed in the background.
The worker containers are not part of the compose project, so docker compose down does not remove
them. To take the installation down completely (auditix being the project name):
docker compose stop orchestratordocker rm -f $(docker ps -aq --filter label=io.auditix.orchestrator.project=auditix)docker compose downBack up
Section titled “Back up”All data lives in PostgreSQL. Back it up regularly, and always before an update by hand:
docker compose exec -T database pg_dump -U auditix -Fc auditix > auditix-$(date +%F).dumpRestore into an empty database:
docker compose exec -T database pg_restore -U auditix -d auditix --clean --if-exists < auditix-YYYY-MM-DD.dumpRecover an administrator account
Section titled “Recover an administrator account”When no administrator can sign in, set a new password from the server:
docker compose exec backend bin/console auditix:user:password admin@example.com