Deploy an App
An app service is created with a single POST /app-services request. The platform provisions the VM, installs and starts the container, sets up the HTTPS ingress and DNS, and drives the app from Pending to Running asynchronously.
Deploy from a git repository
An app can start from a prebuilt image (this page) or from source: point app_config.source at a git repository with a Dockerfile and the platform clones, builds, and deploys it through the same blue/green flow. See Build from Source. Everything else on this page (plans, health checks, updates, deletion) applies to both kinds of app.
Create an app
curl -u "$USER:$PASS" -X POST https://api.foundrydb.com/app-services \
-H "Content-Type: application/json" \
-d '{
"name": "my-api",
"plan_name": "tier-2",
"zone": "se-sto1",
"app_config": {
"image_ref": "ghcr.io/acme/api:1.0.0",
"container_port": 8080,
"env": {
"LOG_LEVEL": "info",
"CACHE_TTL": "300"
}
},
"storage_size_gb": 10
}'
The response is 201 Created with the app in Pending. Poll until Running:
curl -u "$USER:$PASS" https://api.foundrydb.com/app-services/{id}
Once Running, the response includes the url field, for example https://my-api-ab12cd34.foundrydb.com.
Request fields
Top-level
| Field | Required | Description |
|---|---|---|
name | Yes | Name for the app. Becomes part of the foundrydb.com hostname. |
plan_name | Yes | Compute tier. tier-2 (2 vCPU, 4 GB RAM) is a good default. |
zone | No | UpCloud zone. Defaults to se-sto1 (Stockholm). Must share a peering region with any attached databases. |
app_config | Yes | Container configuration (see below). |
storage_size_gb | No | VM disk size in GB. Minimum 10, default 10. |
storage_tier | No | standard or maxiops (NVMe SSD). Default maxiops. |
attached_service_ids | No | UUIDs of managed databases or apps to attach at creation. Up to five. |
app_config
| Field | Required | Default | Description |
|---|---|---|---|
image_ref | Yes | Full OCI image reference, for example docker.io/library/nginx:1.27. | |
container_port | Yes | TCP port the container listens on. Cannot be changed after creation. | |
env | No | {} | Environment variables. Keys must be valid POSIX names and must not start with MDB_ or equal DATABASE_URL. |
custom_domains | No | [] | Additional hostnames beyond {name}.foundrydb.com. Up to five. |
registry_username | No | Username for a private registry. Provide with registry_password. | |
registry_password | No | Password or access token. Write-only: never returned by the API. | |
health_check_path | No | / | HTTP path probed on the new container during a blue/green deploy. Must return 2xx. |
health_check_interval_seconds | No | 2 | Seconds between probe attempts. |
health_check_timeout_seconds | No | 120 | Seconds to wait for the container to become healthy before the deploy fails. |
health_check_healthy_threshold | No | 1 | Consecutive 2xx responses required before traffic cuts over. |
Compute plans
| Plan | vCPU | Memory |
|---|---|---|
tier-1 | 1 | 2 GB |
tier-2 | 2 | 4 GB |
tier-4 | 4 | 8 GB |
tier-6 | 6 | 16 GB |
Prefer tier-2, tier-4, or tier-6. See Scaling and Health for resize behaviour.
Configuring health checks
Health checks gate the blue/green deploy cutover. A container that never becomes healthy within the timeout fails the deploy, and the previous container keeps serving with no interruption.
curl -u "$USER:$PASS" -X POST https://api.foundrydb.com/app-services \
-H "Content-Type: application/json" \
-d '{
"name": "my-api",
"plan_name": "tier-2",
"zone": "se-sto1",
"app_config": {
"image_ref": "ghcr.io/acme/api:1.0.0",
"container_port": 8080,
"health_check_path": "/healthz",
"health_check_timeout_seconds": 60,
"health_check_healthy_threshold": 3
}
}'
The probe runs on the VM loopback against the container's own port, so it does not depend on public DNS or TLS.
Update and redeploy
A PATCH /app-services/{id} replaces app_config wholesale and triggers a zero-downtime blue/green redeploy if image_ref, env, or custom_domains changed. Send the full desired config including any fields you want to keep.
Every redeploy follows this sequence:
- Image pull. The platform pulls the new
image_reffrom the registry before stopping the live container. If the pull fails (missing tag, bad credentials, registry unreachable), the deploy is aborted and the running container is untouched. - Container start and health probe. The new container starts on the same VM alongside the existing one. The platform probes
health_check_pathon the container's port over loopback, repeating everyhealth_check_interval_secondsuntilhealth_check_healthy_thresholdconsecutive2xxresponses are received orhealth_check_timeout_secondselapses. - Ingress cutover. Once the new container passes its health threshold, the nginx ingress rule is atomically rewritten to route traffic to the new container. Existing connections to the old container are allowed to finish.
- Old container drain and stop. After cutover the old container receives a
SIGTERMand is given a short grace period to drain in-flight requests, then is stopped and removed.
A deploy that fails the health check leaves the old container running. The app stays Running and the failed deploy is recorded in the deployment history without any user-visible downtime.
curl -u "$USER:$PASS" -X PATCH https://api.foundrydb.com/app-services/{id} \
-H "Content-Type: application/json" \
-d '{
"app_config": {
"image_ref": "ghcr.io/acme/api:1.1.0",
"container_port": 8080,
"env": { "LOG_LEVEL": "debug" },
"health_check_path": "/healthz"
}
}'
Note: container_port cannot be changed after creation. The app must be Running to accept a PATCH.
Delete an app
curl -u "$USER:$PASS" -X DELETE https://api.foundrydb.com/app-services/{id}
Deletion tears down the container, ingress, certificates, DNS records, floating IP, attachment peerings and firewall openings, and the VM. A database that still has apps attached cannot be deleted until those apps are removed.
Dashboard
The app detail page in the dashboard covers the same lifecycle. The Overview tab shows current status and the public URL. The Configuration tab lets you update the image and environment. The Deployments tab shows the deploy history.