Skip to main content

skiff2.json Reference

skiff2.json lives at the root of your repo and describes the services you want to build and deploy.

Example​

{
"$schema": "https://storage.googleapis.com/skiff2-general/skiff2-config.schema.json",
"services": [
{
"name": "ui",
"cwd": "./ui",
"isRootService": true,
"allowUnauthenticated": true
},
{
"name": "api",
"cwd": "./api",
"secretFiles": {
"my-secret-name": "/secrets/config.json"
}
}
]
}

Top-level fields​

services (required)​

Array of service definitions. At least one service is required.

Type: ServiceConfig[]


remoteServices​

An optional array of remote services that are deployed from repositories other than the main repository for a project.

Type: RemoteServiceConfig[]


projectName​

An optional override for the project name used when naming Cloud Run services and images. Defaults to the repository name.

Type: string


environments​

An optional list of environment names that this project deploys to.

Type: string[]


prodBranch​

The branch whose deploys map to the prod environment. Deploys from this branch use the bare domain, and can route custom domains. It is recommended to not change this once set. The deploy action will run into issues when this is changed and it will require some manual config to fix the deploy.

Type: string Default: "main"


ServiceConfig fields​

name (required)​

A short identifier for this service. Used to derive:

  • The Cloud Run service name
  • The container image name
  • The subdomain for non-root services (e.g. prod-api.my-project.apps.allenai.org)

Type: string


cwd (required)​

Path to the directory containing the Dockerfile, relative to the skiff2.json file.

Type: string

Example: ".", "./ui", "./packages/server"


isRootService​

When true, this service is routed from the project's top-level domains:

  • my-project.pandajungle.org
  • my-project.allen.ai
  • my-project.apps.allenai.org

Only one service should have isRootService: true. Non-root services get their own subdomain prefix (e.g. prod-api.my-project.apps.allenai.org).

Type: boolean Default: false


dockerFile​

Path to the Dockerfile, relative to cwd. Defaults to Dockerfile in the cwd directory.

Type: string


allowUnauthenticated​

Allows unauthenticated requests to this Cloud Run service. Required for public-facing web apps.

Type: boolean Default: false


allowedPrincipals​

GCP IAM members allowed to access this service through IAP. IAP still forces a Google sign-in; this list controls who passes the check after they've signed in.

Type: string[] Default: ["domain:allenai.org"]

Each entry is a literal GCP IAM member identifier. Supported forms:

FormExampleMeaning
domain:"domain:allenai.org"Anyone with an email in this Google Workspace domain
user:"user:alice@example.com"A specific user
group:"group:partners@allenai.org"A Google Group
allAuthenticatedUsers"allAuthenticatedUsers"Anyone signed in with any Google account, including personal @gmail.com

Example (mix a domain with a few outside users):

{
"allowedPrincipals": [
"domain:allenai.org",
"user:contractor@example.com",
"group:partners@allenai.org"
]
}
note

This field is ignored when allowUnauthenticated: true (the service is fully public in that case). To make a service public, use allowUnauthenticated, not "allUsers" here — "allUsers" is rejected by the schema.

allAuthenticatedUsers is broader than it sounds

allAuthenticatedUsers lets in any Google account on the internet, including personal @gmail.com addresses — not just AI2 users. In most cases you want domain:allenai.org plus specific user: or group: exceptions.

allAuthenticatedUsers needs a one-time manual OAuth setup

IAP's default Google-managed OAuth client only permits accounts inside the AI2 org, so allAuthenticatedUsers won't take effect until you switch the service to a custom OAuth client. This is a manual step in the GCP console (Terraform can't do it):

  1. Open the Google Auth overview for your project and fill out the branding form (app name + support contact email).
  2. Open Identity-Aware Proxy, find your service in the list, click the ⋮ menu → Settings, and change Google-Managed OAuth to Custom OAuth.

Add ?project=YOUR_PROJECT_ID to either link to open your project directly. See the Authentication guide for details.


serviceAccount​

The GCP service account that this Cloud Run service runs as. Applies to the main container and all sidecars in the service. When omitted, Cloud Run uses the default compute service account.

Type: string (email)

Format: <ACCOUNT_NAME>@<PROJECT_NAME>.iam.gserviceaccount.com

Example:

{
"serviceAccount": "my-api-runner@my-project.iam.gserviceaccount.com"
}
New service accounts have no permissions

A freshly created service account starts with no IAM bindings. The default Cloud Run service account works out of the box because it has broad project-level access — a custom one will not. If you switch to a new service account, you'll need to grant it access to anything the service uses (Secret Manager secrets, GCS buckets, Cloud SQL, Pub/Sub, etc.) or runtime calls will fail with permission errors.


allowDelete​

Allows this Cloud Run service to be deleted by Terraform. When false, the service is excluded from the load balancer URL map to prevent deletion errors.

Type: boolean Default: false


httpVersion​

The HTTP version to use for this service. When set to "2", the service uses end-to-end HTTP/2 (h2c) between Cloud Run and your container.

Type: "1" | "2" (string) Default: "1"

Example:

{
"httpVersion": "2"
}
caution

Only use HTTP/2 if your service actually supports it. With httpVersion: "2", Cloud Run sends requests to your container over HTTP/2 cleartext (h2c) — your server must speak h2c (HTTP/2 without TLS) or requests will fail. This is typically needed for gRPC or streaming workloads.


secretFiles​

Mounts Secret Manager secrets as files inside the container.

Type: Record<string, string>

The key is the Secret Manager secret name; the value is the mount path inside the container.

Example:

{
"secretFiles": {
"my-app-config": "/secrets/config.json",
"my-app-credentials": "/secrets/credentials.json"
}
}

ephemeralStorage​

Mounts Cloud Run ephemeral disk volumes inside the container. Useful for scratch space like caches or temporary downloads that don't fit in memory.

Type: Record<string, string>

The key is the mount path inside the container; the value is the provisioned size. Sizes use the 10Gi format and can be at most 10Gi per volume.

Example:

{
"ephemeralStorage": {
"/tmp/cache": "5Gi",
"/data/scratch": "10Gi"
}
}
Data is ephemeral

Each instance gets its own empty volume, and the data persists only for the lifetime of that instance. When an instance is shut down (scale-down, redeploy, etc.), its data is gone. Do not use this for anything you need to keep.

Billing and Pre-GA

You are billed for the full provisioned size for the lifetime of each instance, regardless of how much is actually used. Ephemeral disk is also a Pre-GA Cloud Run feature — services that use it are deployed with the BETA launch stage and the gen2 execution environment; all other services stay on GA.


nfsVolumes​

Mounts NFS volumes (e.g. a Filestore instance) inside the container. Unlike ephemeralStorage, an NFS share is shared across all instances and persists independently of them.

Type: Record<string, object>

The key is the mount path inside the container; the value is an object describing the NFS share:

FieldTypeDefaultDescription
serverstring-Hostname or IP of the NFS server (e.g. a Filestore instance IP).
pathstring-The exported path on the NFS server to mount.
readOnlybooleanfalseWhether to mount the share read-only.

Example:

{
"nfsVolumes": {
"/mnt/data": {
"server": "10.0.0.5",
"path": "/exports/data"
},
"/mnt/shared": {
"server": "10.0.0.5",
"path": "/exports/shared",
"readOnly": true
}
}
}
Requires network access

The NFS server must be reachable from the service, which usually means configuring vpc so the service can reach the private IP of the share. Services that use NFS volumes are deployed with the gen2 execution environment.


customDomains​

Additional domains to route to this service. Skiff2 will provision SSL certificates and load balancer routing for each domain listed here.

Type: string[]

There are two cases depending on whether the domain is in a managed zone:

Managed zones (allen.ai, apps.allenai.org) — Skiff2 can manage DNS for you, but you need to do both:

  1. Add the domain to customDomains here (SSL cert + LB routing)
  2. Add the domain to customDomains in your project-config.json (DNS A record)

External/unmanaged domains (e.g. myapp.com) — Only add the domain here. DNS must be pointed at the GCP load balancer IP separately (outside of Skiff2).

Example (external domain):

{
"customDomains": ["myapp.com"]
}

Example (managed zone):

{
"customDomains": ["myapp.allen.ai"]
}
warning

Once you set a custom domain in skiff2.json and deploy, Google will attempt to create an SSL certificate for it. If the domain's DNS is not pointed at the load balancer within an hour, Google's certificate creation will fail. This prevents the SSL certificate from ever being created and resolved.

To avoid this, update DNS at the same time you add the domain to customDomains and deploy. For external domains, point your DNS at the GCP load balancer IP first (or in tandem with the deploy); for managed zones, ensure the DNS A record (step 2 above) lands together with the SSL/LB change.


extraBuildArgs​

Additional --build-arg flags to pass to docker build.

Type: string[]

Each item is passed as a separate argument to docker build. To pass a build arg, include both the flag and the value as separate entries:

Example:

{
"extraBuildArgs": ["--build-arg", "UI_IMAGE=gcr.io/${PROJECT_ID}/${REPO_NAME}-ui:${COMMIT_SHA}"]
}

The tokens ${PROJECT_ID}, ${REPO_NAME}, and ${COMMIT_SHA} are automatically substituted at build time.


dependsOn​

Names of other services that must be built before this one. Controls build order.

Type: string[]


deploy​

When false, this service is built but not deployed to Cloud Run.

Type: boolean Default: true


machine​

Cloud Run machine configuration for this service.

Type: object

FieldTypeDefaultDescription
minInstancesnumber0Minimum number of instances
maxInstancesnumber10Maximum number of instances
memorystring"512Mi"Memory allocation (e.g. "512Mi", "1Gi")
cpunumber1Number of CPUs. Fractional values are allowed (e.g. 0.08, 0.5) — see below
cpuIdlebooleantrueWhether CPU is throttled when not handling requests
warning

Setting minInstances to 0 will result in cold starts for your application.

Fractional CPU values​

cpu follows Cloud Run's CPU rules:

  • Values below 1 can be fractional, from a minimum of 0.08 up to (but not including) 1, in increments of 0.01 (e.g. 0.08, 0.25, 0.5).
  • Values of 1 and above must be whole numbers supported by Cloud Run (1, 2, 4, 6, 8).

Cloud Run also enforces memory/CPU pairing constraints (e.g. more CPUs require more memory) — see the Cloud Run docs linked above for the current limits.

Example:

{
"machine": {
"minInstances": 1,
"maxInstances": 5,
"memory": "1Gi",
"cpu": 2,
"cpuIdle": false
}
}

requestTimeoutSeconds​

The maximum time (in seconds) a request to this service is allowed to take before Cloud Run terminates it. See request timeout.

Type: number Default: 300 (5 minutes)

The maximum allowed value is 3600 (60 minutes). Increase this for long-running requests such as large uploads or streaming responses.

Example:

{
"requestTimeoutSeconds": 600
}

maxConcurrentRequests​

The maximum number of concurrent requests each instance of this service can receive before Cloud Run starts a new instance. See concurrency.

Type: number Default: 80

The maximum allowed value is 1000. Lower this if each request is resource-intensive (so instances scale out sooner); raise it for lightweight, I/O-bound workloads to pack more requests onto each instance.

Example:

{
"maxConcurrentRequests": 120
}

startup​

Type: ProbeConfig

Startup probe configuration for this service

FieldTypeDefaultDescription
initialDelaySecondsnumber-Number of seconds after the container has started before the probe is initiated.
timeoutSecondsnumber-Number of seconds after which the probe times out.
periodSecondsnumber-How often (in seconds) to perform the probe.
failureThresholdnumber-Minimum consecutive failures for the probe to be considered failed after having succeeded.
pathstring/Path to access on the HTTP server.
portnumber8080Port number to access on the container. Must be in the range 1 to 65535.

Example:

{
"startup": {
"initialDelaySeconds": 10,
"timeoutSeconds": 5,
"periodSeconds": 10,
"failureThreshold": 3,
"path": "/api/health",
"port": 8080
}
}

liveness​

Type: ProbeConfig

Liveness probe configuration for this service

FieldTypeDefaultDescription
initialDelaySecondsnumber-Number of seconds after the container has started before the probe is initiated.
timeoutSecondsnumber-Number of seconds after which the probe times out.
periodSecondsnumber-How often (in seconds) to perform the probe.
failureThresholdnumber-Minimum consecutive failures for the probe to be considered failed after having succeeded.
pathstring/Path to access on the HTTP server.
portnumber8080Port number to access on the container. Must be in the range 1 to 65535.

Example:

{
"liveness": {
"initialDelaySeconds": 5,
"timeoutSeconds": 10,
"periodSeconds": 15,
"failureThreshold": 3,
"path": "/api/health",
"port": 8080
}
}

vpc​

Type: Vpc

Vpc configuration for this service. Used to handle internal connections to things like databases or Redis.

FieldTypeDefaultDescription
networkstring-The VPC network that the Cloud Run resource will be able to send traffic to. At least one of network or subnetwork must be specified. If both network and subnetwork are specified, the given VPC subnetwork must belong to the given VPC network. If network is not specified, it will be looked up from the subnetwork.
subnetworkstring-The VPC subnetwork that the Cloud Run resource will get IPs from. At least one of network or subnetwork must be specified. If both network and subnetwork are specified, the given VPC subnetwork must belong to the given VPC network. If subnetwork is not specified, the subnetwork with the same name with the network will be used.
egress`PRIVATE_RANGES_ONLYALL_TRAFFIC`PRIVATE_RANGES_ONLY

Example:

{
"vpc": {
"network": "skiff2-service-vpc",
"subnetwork": "skiff2-service-subnet"
}
}

sidecars​

Type: List of Containers

Allows configuration of sidecars. These will run alongside the main container of this service and can be called by other containers in this service.

warning

Configure a startup check on every sidecar. Cloud Run will route traffic to the main container before a sidecar is ready unless a startup probe gates it — without one, early requests that depend on the sidecar will return 502s.

All properties of a Container share a type with a Service.

FieldLink
namename
cwdcwd
dockerFiledockerFile
dependsOndependsOn
extraBuildArgsextraBuildArgs
secretFilessecretFiles
ephemeralStorageephemeralStorage
nfsVolumesnfsVolumes
machinemachine
startupstartup
livenessliveness
vpcvpc

includeDNSAuthorizationForExternalDomains​

If true, also sets a DNS auth certificate for the custom domains set on this service. This is useful if you want to migrate an existing project without downtime caused by certificate authorization.

If used, the Skiff 2 infra deploy action will output a DNS record you will need to add to the DNS for your app's custom domain.

For example, for this DNS authorization record:

custom_domain_dns_authorizations = [
tolist([
{
"data" = "ad56a6az-5cae-40e5-a2eb-1bf63683583f.3.authorize.certificatemanager.goog."
"name" = "_acme-challenge.my-app.allenai.org."
"type" = "CNAME"
},
]),
]

You would add a CNAME record named _acme-challenge.my-app.allenai.org. with the data ad56a6az-5cae-40e5-a2eb-1bf63683583f.3.authorize.certificatemanager.goog..

RemoteServiceConfig fields​

name (required)​

A short identifier for this service. This must match the name of a service defined in the skiff2.json of a remote repository.

Type: string


customDomains​

Additional domains to route to this service. Skiff2 will provision SSL certificates and load balancer routing for each domain listed here. See the customDomains field in ServiceConfig for details on how routing works for managed vs. external domains.

Type: string[]

includeDNSAuthorizationForExternalDomains​

See includeDNSAuthorizationForExternalDomains in the service config.