Skip to main content

InvenTree Common — Shared Application Configuration

InvenTree_Common is the shared application layer for InvenTree. It is not deployed on its own; instead it supplies the InvenTree-specific configuration that InvenTree_CloudRun builds on — the container image, the baseline InvenTree environment, and the database bootstrap jobs. End users never configure this layer directly — it has no deployment UI of its own — but understanding what it provides explains the defaults you see in the platform guide.

For the infrastructure that actually provisions and runs InvenTree, see the platform guide (InvenTree_CloudRun) and the foundation guides (App_CloudRun, App_Common).


1. What this layer provides​

AreaProvided by InvenTree_CommonWhere it surfaces
Container imageThin custom build of the official inventree/inventree image with a wrapper entrypoint; built via Cloud Buildcontainer_image output of the platform deployment
Database engineCloud SQL for MySQL 8.0 (MYSQL_8_0), INVENTREE_DB_ENGINE = "mysql", port 3306§Database in the platform guide
Database bootstrapA two-job chain: db-init (database, user, grants) → migrate (Django migrations)initialization_jobs output
Core settingsThe baseline INVENTREE_* environment: proxy/HTTPS handling, data directory, migrations off in-processApplication behaviour in the platform guide
StorageOne extra Cloud Storage bucket (storage)storage_buckets output
SecretsNone — the only credential is the database password, generated by the foundationdatabase_password_secret output
Health checksPasses through the variant's startup_probe / liveness_probe§Observability in the platform guide

The module also enables the Secret Manager API on the project.


2. Secrets​

InvenTree_Common generates no application secrets: its secret_ids and secret_values outputs are both empty. The database password is generated and stored in Secret Manager by the foundation and injected as DB_PASSWORD.

InvenTree's own signing key is not supplied by this layer. InvenTree generates secret_key.txt inside its data directory (/home/inventree/data) on first start, which is why that directory must be persistent — see §6.

See App_Common for the shared secret model.


3. Database engine and bootstrap​

InvenTree runs on Cloud SQL for MySQL 8.0 (MYSQL_8_0). InvenTree ships no bundled SQL — the schema is created by Django migrations. Two initialization jobs run in sequence (both execute_on_apply = true):

  1. db-init (mysql:8.0-debian, max_retries = 3, timeout_seconds = 600) — finds the Cloud SQL connection (a Unix socket under /cloudsql if mounted, otherwise TCP to DB_IP), waits for MySQL, creates or aligns the application user and database, grants privileges, verifies the user can connect (which also warms the caching_sha2_password cache), then signals the Cloud SQL Auth Proxy sidecar to exit.
  2. migrate (depends_on_jobs = ["db-init"], the application image, cpu_limit = "2000m", memory_limit = "2Gi", timeout_seconds = 1800, max_retries = 1, mount_nfs = true) — maps DB_* onto INVENTREE_DB_*, runs python3 manage.py migrate --noinput, then counts the tables actually present and fails if there are fewer than 10. A migrate that silently picked up the wrong settings would otherwise exit 0 with an empty schema.

A caller-supplied initialization_jobs list replaces both jobs.

Recovery note. A database left half-migrated (for example by an in-process migration attempt) cannot be repaired by re-running migrate — it fails with Duplicate column name. Drop and recreate the database, then run db-init and migrate again.


4. Container image and entrypoint​

scripts/Dockerfile:

ARG INVENTREE_VERSION=1.5.4
FROM inventree/inventree:${INVENTREE_VERSION}

COPY entrypoint.sh /cloud-entrypoint.sh
RUN chmod +x /cloud-entrypoint.sh

ENTRYPOINT ["/cloud-entrypoint.sh"]
CMD ["sh", "-c", "exec gunicorn -c ./gunicorn.conf.py InvenTree.wsgi -b ${INVENTREE_WEB_ADDR}:${INVENTREE_WEB_PORT} --chdir ${INVENTREE_BACKEND_DIR}/InvenTree"]

The CMD is restated deliberately: the vendor image keeps the gunicorn command in CMD, so replacing the ENTRYPOINT without it left init.sh with no arguments and the container exited 0 on every start.

The base tag comes from the app-specific INVENTREE_VERSION build ARG, set from application_version. The pin that takes effect is the variant's application_version default (1.5.4 in InvenTree_CloudRun), because the variant passes its value down to this layer.

scripts/entrypoint.sh runs on every container start:

  • Site URL. If INVENTREE_SITE_URL is unset, it falls back to CLOUDRUN_SERVICE_URL; if both are missing it refuses to start. InvenTree's settings.py exits on start without a site URL (URL validation, empty ALLOWED_HOSTS, empty CSRF_TRUSTED_ORIGINS).
  • Database mapping. Requires DB_IP, DB_NAME, DB_USER, DB_PASSWORD and exports INVENTREE_DB_HOST (from DB_IP — the socket-directory form of DB_HOST is not a TCP host), INVENTREE_DB_NAME, INVENTREE_DB_USER, INVENTREE_DB_PASSWORD and INVENTREE_DB_PORT (default 3306).
  • Static files. Unless INVENTREE_COLLECTSTATIC is false, runs invoke int.collect-static --no-i18n (falling back to manage.py collectstatic --noinput). A failure is a warning, not a fatal error.
  • Hand-off. exec /bin/bash ./init.sh "$@" — the vendor init script then runs gunicorn (web container) or invoke worker (the qcluster sidecar).

scripts/migrate.sh performs the same DB_* mapping for the migrate job.


5. Core application settings​

InvenTree_Common sets this baseline environment. Values in the caller's environment_variables are merged on top and win on a clash.

VariableValueWhy
INVENTREE_DB_ENGINEmysqlCloud SQL for MySQL.
INVENTREE_DB_PORT3306MySQL port.
INVENTREE_AUTO_UPDATEfalseThe migrate job owns the schema. In-process migration runs before the port binds, its empty-database branch upstream is unreachable, and two containers would race.
INVENTREE_USE_X_FORWARDED_PROTOtrueOtherwise every absolute link (password reset, invitations) is built as http:// behind Cloud Run.
INVENTREE_SESSION_COOKIE_SECUREtrueOtherwise CSRF, session and language cookies are non-secure on an HTTPS-only endpoint.
INVENTREE_DATA_DIR/home/inventree/dataAll mutable state.
INVENTREE_BACKGROUND_WORKERS2From this layer's background_workers input (not exposed by the variant).

Container defaults set here: container_port = 8000 (gunicorn binds INVENTREE_WEB_ADDR:INVENTREE_WEB_PORT, not Cloud Run's $PORT), database_type = MYSQL_8_0, enable_cloudsql_volume = false, cloudsql_volume_mount_path = /cloudsql, image_source = "custom", plus the resource limits and instance counts forwarded from the variant.

Inputs declared here but not used. admin_username, admin_email, php_memory_limit and enable_gcs_storage_volume are declared (and the last three are forwarded by the variant) but nothing in this layer reads them. No administrator account is created and no bucket is mounted by them.


6. Storage and the data directory​

InvenTree keeps all of its mutable state under INVENTREE_DATA_DIR (/home/inventree/data): config.yaml, the generated secret_key.txt, uploaded media, collected static files and the plugin directory. The image declares no volume for it.

This layer adds no GCS Fuse volume for it — a real POSIX filesystem (the NFS share) is the intended backing store. In the variant, the qcluster sidecar mounts NFS at /home/inventree/data and the web container and jobs mount it at nfs_mount_path, all only when enable_nfs = true. Any caller-supplied gcs_volumes are passed through unchanged.

The storage_buckets output adds one bucket with the suffix storage (STANDARD, force_destroy = true, public access prevention enforced). It is not mounted into the container.


7. Health probe behaviour​

This layer forwards whatever startup_probe / liveness_probe the variant passes. In InvenTree_CloudRun both are HTTP GET /, matching the vendor image's own health check; the startup probe allows a long window (failure_threshold = 60, period_seconds = 15).


For the InvenTree-specific, user-facing configuration (variables by group, outputs, and how to explore each service from the Console and CLI), see the platform guide: InvenTree_CloudRun.

Need RAD to do something it does not do yet? Request it on the roadmap, or vote on what is already there.