Files

7.5 KiB

Installing ITWorx ModelForge v1.2.1

This is the whole installation. If you have to do something that is not written here, that is a defect — please report it rather than working around it.

The steps below are release-gated against a fresh project with isolated volumes, newly generated test secrets and an empty PostgreSQL database.

1. What you need

Requirement Minimum Notes
Docker Engine 24 docker version
Docker Compose 2.x docker compose version
PostgreSQL 17 (16 accepted) Provided by the deployment; you do not install it separately
Redis 7 Provided by the deployment
Disk 50 GiB free, more for model artifacts Model weights dominate; plan for the models you intend to run
Python 3.12 Only to run the preflight, bootstrap and upgrade tools from the host

A GPU is not required on the control-plane host. It is required on any compute node that will serve a capability, along with the NVIDIA driver and the NVIDIA container runtime. See NODE_AGENT.md.

Networking: the control plane never dials a compute node. Nodes report outbound to the control plane, so a node needs to reach the API but the API needs no route back.

PostgreSQL 16+ is the production database contract. SQLite is an isolated unit-test backend, not a supported installation or migration target.

2. Check the host first

python scripts/preflight.py

It reports the Docker and Compose versions, the NVIDIA driver if present, whether the ports the deployment publishes are free, and free disk. Exit status is 0 when the host can run ModelForge.

Nothing is changed by this command.

3. Generate your secrets

ModelForge never mints its own credentials. A platform that generates its own admin secret has no way to tell you it did.

python -c "import secrets; print(secrets.token_urlsafe(48))"          # operator API key
python -c "import base64, os; print(base64.b64encode(os.urandom(32)).decode())"  # backup key
python -c "import secrets; print(secrets.token_urlsafe(32))"          # PostgreSQL bootstrap admin
python -c "import secrets; print(secrets.token_urlsafe(32))"          # migration/schema owner
python -c "import secrets; print(secrets.token_urlsafe(32))"          # API runtime role

Store the backup encryption key outside this deployment. Losing it makes every existing backup unrecoverable. It is the one value that must survive the machine it protects.

4. Configure

cp .env.example .env

Fill in at least these. Production refuses to start without them:

MODELFORGE_POSTGRES_DB=modelforge
MODELFORGE_POSTGRES_ADMIN_USER=postgres
MODELFORGE_POSTGRES_ADMIN_PASSWORD=<generated-admin-secret>
MODELFORGE_MIGRATION_DB_PASSWORD=<generated-owner-secret>
MODELFORGE_RUNTIME_DB_PASSWORD=<generated-runtime-secret>
MODELFORGE_MIGRATION_DATABASE_URL=postgresql+psycopg://modelforge:<URL-encoded-owner-secret>@postgres:5432/modelforge
MODELFORGE_RUNTIME_DATABASE_URL=postgresql+psycopg://modelforge_runtime:<URL-encoded-runtime-secret>@postgres:5432/modelforge
MODELFORGE_OPERATOR_API_KEY=<generated, at least 32 characters>
MODELFORGE_BACKUP_ENCRYPTION_KEY=<generated base64 AES-256 key>
MODELFORGE_CORS_ORIGINS=https://modelforge.example.internal
VITE_API_BASE_URL=https://modelforge.example.internal:8000
MODELFORGE_VERSION=1.2.1
MODELFORGE_API_IMAGE=modelforge-api:1.2.1
MODELFORGE_WEB_IMAGE=modelforge-web:1.2.1

Every setting is documented in CONFIGURATION.md, which is generated from the typed settings — it cannot drift from what the code actually reads.

To move a port, set MODELFORGE_API_PUBLISHED_PORT, MODELFORGE_WEB_PORT, MODELFORGE_POSTGRES_PORT or MODELFORGE_REDIS_PORT. You never have to edit a Compose file.

Then re-run the preflight with the production rules applied:

python scripts/preflight.py --production

5. Deploy

With the verified release images already loaded or pulled, deploy them without rebuilding:

docker compose -f docker-compose.yml -f docker-compose.production.yml up -d --no-build

docker-compose.yml on its own is a development deployment, but it still requires distinct database secrets; none is embedded in the repository. It leaves the environment at development, which switches off production-only startup rules. The production overlay sets MODELFORGE_ENV=production and takes each secret through ${VAR:?message}, so Compose refuses to render the project at all if one is missing. If you see a message like

required variable MODELFORGE_OPERATOR_API_KEY is missing a value

that is the deployment refusing before it starts a single container.

For an explicitly local source build, keep MODELFORGE_VERSION, MODELFORGE_COMMIT and MODELFORGE_BUILT_AT pinned and use up -d --build. The production projection retains this build path, but never derives a deployable image from a floating latest tag.

To stamp build identity into the images so a running container can say where it came from:

MODELFORGE_VERSION=1.2.1 \
MODELFORGE_COMMIT=$(git rev-parse HEAD) \
MODELFORGE_BUILT_AT=$(date -u +%FT%TZ) \
docker compose -f docker-compose.yml -f docker-compose.production.yml up -d --build

6. Verify

curl -s http://127.0.0.1:8000/api/v1/health/live      # 200
curl -s http://127.0.0.1:8000/api/v1/health/ready     # 200
curl -s http://127.0.0.1:8000/api/v1/version          # version, commit, compatibility
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8000/api/v1/admin/recovery/dashboard  # 401
curl -s -o /dev/null -w '%{http_code}\n' -H "X-ModelForge-Admin-Token: $KEY" \
     http://127.0.0.1:8000/api/v1/admin/recovery/dashboard                                       # 200
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3000/                                  # 200

The 401 matters as much as the 200: it is the operator boundary refusing an unauthenticated caller.

The migrate container runs Alembic once with the non-superuser modelforge schema-owner URL and must complete before the API starts. The API receives only the modelforge_runtime URL; it cannot read the owner or bootstrap secret. Migration 0024 grants that runtime role SELECT on the audit tables and EXECUTE on the canonical append function while revoking direct audit mutation and DDL. Production startup re-reads the PostgreSQL catalog and refuses any drift in that boundary.

To run bootstrap tooling yourself, or to check what a clean database would do:

python scripts/bootstrap.py --database-url postgresql+psycopg://user:pass@127.0.0.1:5432/modelforge

bootstrap.py is safe to run twice. Every step either creates what is missing or confirms what is already there and says which. Running it repeatedly on an installed system changes nothing — the release rehearsal ran it three times and all ten authoritative counts were identical.

7. Next

What a fresh install does not include

A newly installed control plane has no capability contracts and no deployments. That is deliberate: contracts are lifecycle-owned and a deployment requires a model artifact that has been acquired, verified and promoted. The projects and policies from the shipped manifests are seeded, and the bindings that wait for a contract are reported rather than fabricated. See FIRST_RUN.md.