The Infersec self-hosted edition combines the console and API into a single service that you run on your own hardware.
Running with Docker
The self-hosted image is published as a single container that bundles the console UI, the public API, and the resource management layer. It is multi-arch (linux/amd64, linux/arm64) and available from the public registry. The :latest tag always points at the current release; pin to a specific version (e.g. :1.83.0) for reproducible deployments.
docker run -d \
--name infersec-selfhosted \
--hostname infersec-selfhosted \
-p 9507:9507 \
-e SELFHOSTED_PUBLIC_URL=https://selfhosted.example.com \
-e LICENSE_KEY=your-license-key \
-e SESSION_SECRET=your-session-secret \
-e DB_PRIMARY_HOST=... \
-e DB_PRIMARY_PORT=... \
-e DB_PRIMARY_DATABASE=... \
-e DB_PRIMARY_USER=... \
-e DB_PRIMARY_PASSWORD=... \
-e REDIS_HOST=... \
-e REDIS_PORT=... \
-e REDIS_PASSWORD=... \
-e REDIS_DB=... \
-e REDIS_LOCK_DB=... \
-e REDIS_API_CACHE_DB=... \
rg.nl-ams.scw.cloud/infersec-public/selfhosted:latest
Or with Compose. This example bundles the three dependencies the instance needs - a MySQL-compatible database (MariaDB), Redis, and an S3-compatible gateway (Versity, see S3 (model storage)) - alongside the self-hosted service:
services:
mariadb:
image: mariadb:12
environment:
MARIADB_DATABASE: infersec
MARIADB_PASSWORD: infersec-password
MARIADB_ROOT_PASSWORD: root-password-change-me
MARIADB_USER: infersec
volumes:
- mariadb-data:/var/lib/mysql
redis:
image: bitnami/redis:latest
environment:
REDIS_PASSWORD: redis-password-change-me
volumes:
- redis-data:/bitnami/redis/data
versity:
command: ["mkdir -p /data/infersec && exec versitygw --port :7070 posix /data"]
environment:
ROOT_ACCESS_KEY: versity-access-key
ROOT_SECRET_KEY: versity-secret-key
entrypoint: ["/bin/sh", "-c"]
image: versity/versitygw:latest
volumes:
- versity-data:/data
selfhosted:
image: rg.nl-ams.scw.cloud/infersec-public/selfhosted:latest
container_name: infersec-selfhosted
depends_on: [mariadb, redis, versity]
hostname: infersec-selfhosted
ports:
- "9507:9507"
environment:
DB_PRIMARY_DATABASE: infersec
DB_PRIMARY_HOST: mariadb
DB_PRIMARY_PASSWORD: infersec-password
DB_PRIMARY_PORT: "3306"
DB_PRIMARY_USER: infersec
HTTPS: "true"
INFERSEC_SECRET_MASTER_KEY_PATH: /data/infersec/master.key
LICENSE_KEY: your-license-key
REDIS_API_CACHE_DB: "2"
REDIS_DB: "0"
REDIS_HOST: redis
REDIS_LOCK_DB: "1"
REDIS_PASSWORD: redis-password-change-me
REDIS_PORT: "6379"
S3_ACCESS_KEY_ID: versity-access-key
S3_BUCKET: infersec
S3_ENDPOINT: http://versity:7070
S3_REGION: us-east-1
S3_SECRET_ACCESS_KEY: versity-secret-key
SELFHOSTED_PUBLIC_URL: https://selfhosted.example.com
SESSION_SECRET: your-session-secret
volumes:
- selfhosted-data:/data/infersec
volumes:
mariadb-data:
redis-data:
selfhosted-data:
versity-data:
The instance requires a MySQL-compatible database, Redis, and (for model storage) an S3-compatible service. Provide your LICENSE_KEY (issued from the Self Hosted - Licenses section of the console) before starting the container. The selfhosted-data volume keeps the generated secret master key (INFERSEC_SECRET_MASTER_KEY_PATH) stable across container recreation — without it, previously encrypted values become unreadable after a redeploy.
Keep the container identity stable. The license validation fingerprint includes the container hostname. Set a fixed
hostname(andcontainer_name) and preserve it across redeployments — do not let it be generated randomly. Running the sameLICENSE_KEYunder a different hostname within 3 hours is detected as use on a second instance and will automatically blacklist the license. If you are migrating hosts, decommission the old container first.
Environment variables
| Variable | Purpose | Required |
|---|---|---|
PORT |
Service port (default 9507) | Yes |
SELFHOSTED_PUBLIC_URL |
Public URL for template substitution | Yes |
SELFHOSTED_ALLOWED_HOSTS |
Host whitelist (comma-separated) | No |
HTTPS |
Marks the session cookie as secure (HTTPS-only). Defaults to true; set to false for plain-HTTP dev |
No |
LICENSE_KEY |
License key issued from the console | Yes |
SESSION_SECRET |
Session encryption secret | Yes |
DB_PRIMARY_DATABASE |
MySQL database name | Yes |
DB_PRIMARY_HOST |
MySQL host | Yes |
DB_PRIMARY_PASSWORD |
MySQL password | Yes |
DB_PRIMARY_PORT |
MySQL port | Yes |
DB_PRIMARY_USER |
MySQL user | Yes |
REDIS_API_CACHE_DB |
Redis database for API response caching | Yes |
REDIS_DB |
Redis database number | Yes |
REDIS_HOST |
Redis host | Yes |
REDIS_LOCK_DB |
Redis database for locking | Yes |
REDIS_PASSWORD |
Redis password | Yes |
REDIS_PORT |
Redis port | Yes |
DB_SEED |
Seed database on startup (defaults to false) |
No |
DB_SEED_EMAIL |
Seeded user email. Required when DB_SEED=true |
No |
DB_SEED_NAME |
Seeded user display name. Required when DB_SEED=true |
No |
DB_SEED_PASSWORD |
Seeded user password. Required when DB_SEED=true |
No |
EMAIL_* |
SMTP configuration. All vars must be set together — see Email (SMTP) below | No |
HUGGINGFACE_API_KEY |
HuggingFace API key | No |
INFERSEC_SECRET_MASTER_KEY |
64-hex-character master key value. Overrides the path-based file (used by the SaaS platform) | No |
INFERSEC_SECRET_MASTER_KEY_PATH |
Path to the master key file (auto-generated on first boot). Required when INFERSEC_SECRET_MASTER_KEY is unset; must be on a writable, persistent volume |
Conditional |
S3_* |
S3 storage configuration. All vars must be set together — see S3 (model storage) below | No |
Email (SMTP)
| Name | Required | Default | Note |
|---|---|---|---|
EMAIL_ACTION_BASE_URL |
All-or-nothing | — | Base URL for links inside emails (e.g. password reset) |
EMAIL_FROM_ADDRESS |
All-or-nothing | — | Sender email address |
EMAIL_FROM_NAME |
All-or-nothing | — | Sender display name |
EMAIL_HOST |
All-or-nothing | — | SMTP host |
EMAIL_PASSWORD |
All-or-nothing | — | SMTP password |
EMAIL_PORT |
All-or-nothing | — | SMTP port (integer) |
EMAIL_SECURE |
All-or-nothing | — | true for TLS on connect (port 465), false for STARTTLS |
EMAIL_USER |
All-or-nothing | — | SMTP username |
All EMAIL_* variables must be set together. If any are missing, outbound email is disabled and the queue drainer no-ops.
S3 (model storage)
| Name | Required | Default | Note |
|---|---|---|---|
S3_ACCESS_KEY_ID |
All-or-nothing | — | S3 access key |
S3_BUCKET |
All-or-nothing | — | Bucket name |
S3_SECRET_ACCESS_KEY |
All-or-nothing | — | S3 secret key |
S3_ENDPOINT |
All-or-nothing | — | S3-compatible endpoint URL |
S3_REGION |
All-or-nothing | — | Bucket region |
All S3_* variables must be set together. If any are missing, S3 storage is disabled and database seeding of models is skipped.
If you are not using a cloud S3 provider, Versity is a self-hostable S3-compatible gateway that serves a plain filesystem over the S3 API. It is stateless and pairs well with a single-node deployment. Run it with the posix backend, pointing S3_ENDPOINT at the gateway and pre-creating the configured bucket as a directory:
docker run -d \
--name versity \
-p 7070:7070 \
-e ROOT_ACCESS_KEY=versity-access-key \
-e ROOT_SECRET_KEY=versity-secret-key \
-v versity-data:/data \
--entrypoint /bin/sh \
versity/versitygw:latest \
-c "mkdir -p /data/infersec && exec versitygw --port :7070 posix /data"
Then set S3_BUCKET=infersec and the matching access/secret keys. The S3_ENDPOINT=http://versity:7070 form (service-name hostname) applies to the Compose example above, where the services share a network. When running the gateway and the self-hosted service as separate docker run containers, the versity name does not resolve between them — either attach both containers to a shared user-defined network (docker network create infersec, then --network infersec on both) or point S3_ENDPOINT at the host's address (for example http://host.docker.internal:7070).
Licensing and legitimate use
Each self-hosted license is intended for a single deployment on a single server. When your instance starts, it validates its license key with the Infersec licensing service and reports a fingerprint made up of its container name, a persisted instance ID, and its source IP address.
- One license key covers one running instance.
- If two or more distinct instances (different IP address, container name, or instance ID) validate the same key within a three-hour window, the key is automatically blacklisted.
- A blacklisted key behaves exactly like an invalid key: the instance will fail to start or will shut down.
You can submit an appeal from the Self Hosted - Licenses section of your Infersec console. Blacklist state can only be cleared by Infersec staff via the admin tooling.
If you need to relocate your deployment, stop the old instance and wait at least three hours (the fingerprint retention window) before starting a new one on the same key. Stopping the old instance does not immediately remove its fingerprint from the recent-window count — only after the window elapses is the key free to validate from a new host/container identity. If you cannot wait, request a manual reset via the appeal process above.
Setting a stable container name
Because the container name forms part of the license fingerprint, you must assign a stable hostname to your container. Without a stable name, each redeploy produces a new container name and, combined with the persisted instance ID, can trip the duplicate-detection rule within the three-hour window.
Set a fixed hostname when running the container:
docker run --hostname infersec-selfhosted ...
Or in Compose:
services:
selfhosted:
container_name: infersec-selfhosted
hostname: infersec-selfhosted
Behaviour when the licensing service is unreachable
- At startup, the instance must reach the licensing service successfully. If it cannot, it will not start.
- While running, if the licensing service becomes temporarily unreachable, the instance continues to serve requests for a 72-hour grace period. If the service remains unreachable beyond that window, the instance shuts down.
- A license that is reported as invalid, expired, or blacklisted causes an immediate shutdown.
Plans and limits
Each self-hosted tier caps the number of active endpoints, connected sources, active tool services, and simultaneous requests per endpoint. See the pricing page for the current tiers and limits. Bypassing or attempting to bypass these limits is prohibited under the Terms of Use and may result in immediate license suspension.