Skip to main content

Overview

Every Tinfoil Container requires a tinfoil-config.yml file in the root of your GitHub repository. This file defines the enclave runtime, resource allocation, container configuration, and request routing. For a pre-filled tinfoil-config.yml, follow along with the quickstart guide.

File location

The file must be named tinfoil-config.yml and placed at the root of your repository. Tinfoil fetches this file from GitHub when you deploy or validate a container. The repository must be public. Tinfoil measures your config at each tag and publishes the measurement to the Sigstore transparency log, so clients can verify against it. The Docker image the config references can still be private — see Private images.

Top-level fields

FieldTypeRequiredDescription
cvm-versionstringYesConfidential VM version (e.g. 0.7.5)
cpusintegerYesNumber of CPU cores
memoryintegerYesRAM in megabytes
gpusintegerNoNumber of GPUs to attach to the enclave (1 or 8). Omit for CPU-only workloads.
modelslistNoVerified model-weight artifacts to mount in the enclave. See Model weights.
containerslistYesOne or more container definitions
shimobjectYesPort and path routing configuration

Valid resource values

ResourceValid values
CPUs2, 4, 8, 16, 32
Memory (MB)8192, 16384, 32768, 65536, 131072
GPUs1 or 8 at the top level; each GPU container also needs runtime: nvidia and gpus: all
Memory values correspond to 8 GB, 16 GB, 32 GB, 64 GB, and 128 GB respectively.

Container spec

Each entry in the containers list defines a container to run inside the enclave.
FieldTypeRequiredDescription
namestringYesContainer identifier
imagestringYesDocker image with SHA256 digest (e.g. image:tag@sha256:...)
commandlistNoCommand arguments passed to the container
entrypointlistNoOverride the container’s entrypoint
envlistNoEnvironment variables (see below)
secretslistNoSecret names — values are set in the dashboard
runtimestringNoContainer runtime — set to nvidia for GPU workloads
gpusstring/intNoGPU allocation for this container — typically all. See GPU configuration.
ipcstringNoIPC mode — set to host
volumeslistNoBind mounts (e.g. /mnt/ramdisk/data:/data)
healthcheckobjectNoDocker-style healthcheck run inside the container. See Healthchecks.
restartstringNoRestart policy if the container process exits. One of no (default), always, on-failure, unless-stopped. See Restart policy.

GPU configuration

GPU workloads use a two-step allocation:
  1. Attach GPUs to the enclave with the top-level gpus field, set to either 1 or 8. NVIDIA confidential computing restricts enclaves to those two sizes.
  2. Expose GPUs to a container with runtime: nvidia and a container-level gpus value. Use gpus: all to give the container every GPU attached to the enclave — this is the right choice for both single-GPU and most multi-GPU setups. Individual indices (e.g. gpus: "0,1") are only needed when running multiple containers in an 8-GPU enclave and splitting GPUs between them.
GPU containers typically also set ipc: host so the NVIDIA runtime can share memory with host processes.
Container images must include a SHA256 digest (e.g. image:tag@sha256:...). This pins the exact image binary and ensures it can be verified in the transparency log. To get the digest: docker pull <image> && docker inspect --format='{{index .RepoDigests 0}}' <image>
Pin digests behind an immutable tag (e.g. v1.2.3), not just :latest — on ghcr.io the digest can be garbage-collected once :latest moves.

Model weights

GPU inference containers often need large model weights in addition to the Docker image that runs the server. Use the top-level models list to mount verified model-weight artifacts prepared in the dashboard’s Models tab.
tinfoil-config.yml
models:
  - name: "gemma-4-31b-it"
    repo: "google/gemma-4-31B-it@419b2efe421994fdfd3394e621983d4cc511cd4f"
    mpk: "0900ca6b913db0036792149d3ea5862986d66a6964b010e998f56fbb7e1276ab_62578683904_59fe9787-ed93-577a-9fd9-a7804c932a11"
FieldTypeRequiredDescription
namestringYesLocal identifier for the model artifact
repostringYesHugging Face repo pinned to a commit, in owner/model@commit form
mpkstringYesModel package metadata generated by the Models tab
The model is mounted read-only at /tinfoil/mpk/mpk-<root_hash>, where <root_hash> is the first segment of the mpk value. For a vLLM container, pass that path as --model. See Model weights for the full dashboard workflow and a complete vLLM example.

Environment variable formats

Environment variables support two formats:
env:
  - PORT: "8080"          # Hardcoded value (YAML map syntax)
  - LOG_LEVEL: "info"

secrets:
  - DATABASE_URL           # Looked up from your org's secrets store
  - API_KEY

Healthchecks

Add a healthcheck block to a container to have the enclave verify it’s actually ready before the deployment transitions to Running. Without one, the container is considered ready the moment Docker starts it — fine for fast-starting apps, but a problem for workloads with long startup (model loading, cache warm-up) where the process is up but isn’t serving yet.
containers:
  - name: "inference"
    image: "vllm/vllm-openai:v0.14.1@sha256:..."
    command: ["--port", "8001"]
    healthcheck:
      test: ["CMD", "curl", "-sf", "http://localhost:8001/health"]
      interval: 30s
      timeout: 5s
      start_period: 30m
FieldTypeDescription
testlistCommand to run inside the container. Prefix with CMD to exec directly, or CMD-SHELL to run through a shell. Exit code 0 = pass, non-zero = fail.
intervaldurationHow often to run the check (e.g. 30s). Docker default: 30s.
timeoutdurationHow long a single check can take before it’s counted as a failure (e.g. 5s). Docker default: 30s.
retriesintegerConsecutive failures after start_period before the container is marked unhealthy. Docker default: 3.
start_perioddurationGrace period after container start during which failures don’t count toward retries (e.g. 30m). Docker default: 0s.
How it’s used during boot. The enclave’s boot process polls Docker’s health state every 5 seconds once the container starts and waits until Docker reports the container Healthy before finishing boot. If Docker reports Unhealthy (i.e. retries consecutive failures after start_period has elapsed), the deployment fails and the last healthcheck output is surfaced as the error detail. The test command runs inside the container, so whatever you invoke (curl, wget, a language runtime) has to be available in the image. For an inference server like vLLM that already exposes /health, a curl -sf http://localhost:<port>/health check is idiomatic.
start_period is usually the most important field. If your container takes 15 minutes to load model weights, set start_period to at least 20 minutes — otherwise failing checks during the load phase will burn through retries and the deployment will fail before your app ever gets a chance to serve.
See also. The schema is taken from Docker Compose — see the Compose healthcheck reference for the full semantics (exit codes, CMD-SHELL vs CMD, disabling an inherited check with disable: true).

Restart policy

By default, a container whose process exits stays exited. Set restart to have Docker automatically restart the process if it crashes — useful for long-running servers that should stay up across transient failures.
containers:
  - name: "inference"
    image: "vllm/vllm-openai:v0.14.1@sha256:..."
    restart: always
ValueBehavior
noDon’t restart. This is the default when restart is omitted.
alwaysRestart the container regardless of exit status.
on-failureRestart only if the process exits with a non-zero status.
unless-stoppedLike always, but don’t restart if the container was stopped explicitly.
Interaction with healthchecks. The restart policy fires when the container process exits — it has no effect when Docker marks the container Unhealthy (the process keeps running; only its health state changes). During boot, the enclave fails the deployment on Unhealthy regardless of restart. Once the container has been declared Healthy, restart governs what happens if the process later dies. See also. Taken from Docker Compose — see the Compose restart reference.

Routing

The shim section controls which ports and paths your container exposes.
FieldTypeRequiredDescription
shim.upstream-portintegerYesPort your container listens on
shim.pathslistYesURL paths to expose (supports * wildcards)
Only listed paths are reachable from outside the enclave. Any request to an unlisted path is rejected with a 404 error code.

Examples

Minimal config

tinfoil-config.yml
cvm-version: 0.7.5
cpus: 2
memory: 8192

containers:
  - name: "api"
    image: "ghcr.io/myorg/api-server:v1.0.0@sha256:abc123..."
    command: ["--port", "8000"]

shim:
  upstream-port: 8000
  paths:
    - /health
    - /api/*

With environment variables and secrets

tinfoil-config.yml
cvm-version: 0.7.5
cpus: 4
memory: 16384

containers:
  - name: "api"
    image: "ghcr.io/myorg/api-server:v2.1.0@sha256:def456..."
    env:
      - PORT: "8080"
      - LOG_LEVEL: "info"
      - NODE_ENV: "production"
    secrets:
      - DATABASE_URL
      - STRIPE_SECRET_KEY
    command: ["--port", "8080"]

shim:
  upstream-port: 8080
  paths:
    - /health
    - /api/*
    - /webhooks/*

GPU inference server (vLLM)

tinfoil-config.yml
cvm-version: 0.7.5
cpus: 16
memory: 65536
gpus: 1

models:
  - name: "gemma-4-31b-it"
    repo: "google/gemma-4-31B-it@419b2efe421994fdfd3394e621983d4cc511cd4f"
    mpk: "0900ca6b913db0036792149d3ea5862986d66a6964b010e998f56fbb7e1276ab_62578683904_59fe9787-ed93-577a-9fd9-a7804c932a11"

containers:
  - name: "inference"
    image: "vllm/vllm-openai:v0.14.1@sha256:6fc52be4609fc19b09c163be2556976447cc844b8d0d817f19bc9e1f44b48d5a"
    runtime: nvidia
    gpus: all
    ipc: host
    command: [
      "--model", "/tinfoil/mpk/mpk-0900ca6b913db0036792149d3ea5862986d66a6964b010e998f56fbb7e1276ab",
      "--served-model-name", "gemma-4-31b-it",
      "--port", "8001"
    ]

shim:
  upstream-port: 8001
  paths:
    - /v1/chat/completions
    - /v1/completions
    - /health
    - /metrics

Multi-container setup

tinfoil-config.yml
cvm-version: 0.7.5
cpus: 8
memory: 32768

containers:
  - name: "api"
    image: "ghcr.io/myorg/api-server:v1.0.0@sha256:abc123..."
    env:
      - PORT: "8080"
    secrets:
      - DATABASE_URL
    command: ["--port", "8080"]
  - name: "worker"
    image: "ghcr.io/myorg/worker:v1.0.0@sha256:def456..."
    env:
      - QUEUE_URL: "redis://localhost:6379"
    secrets:
      - API_SECRET

shim:
  upstream-port: 8080
  paths:
    - /health
    - /api/*

Validation

You can validate your config before deploying. In the dashboard, click New Container and select your repo and tag. Tinfoil fetches your tinfoil-config.yml and checks that:
  • CPU, memory, and GPU values are valid
  • Resource usage is within your org’s limits
  • Model weight references are valid when models is set
  • Referenced secrets exist in your org
  • The container image is accessible and includes a SHA256 digest
If validation fails, the dashboard shows specific error messages explaining what needs to be fixed.

Container naming constraints

ConstraintRule
Allowed charactersLowercase letters, numbers, hyphens
Start/endCannot start or end with a hyphen
Max length64 characters
UniquenessUnique per organization (production and debug namespaces are separate)

Examples

NameValid?
my-apiYes
data-processor-v2Yes
MyAPINo — uppercase not allowed
my_apiNo — underscores not allowed
my apiNo — spaces not allowed
-my-apiNo — cannot start with a hyphen
my-api-No — cannot end with a hyphen

Template

The tinfoil-containers-template repo contains a ready-to-use tinfoil-config.yml with the latest cvm-version value. Create a new repo from this template to get started quickly.