---
title: "Deploy a container on a site"
method: POST
path: "/containers"
tags: ["Containers"]
---

# Deploy a container on a site

`POST /containers`

## Request

- Base URL: `https://api.hypamoon.com/api/v1`
- URL: `https://api.hypamoon.com/api/v1/containers`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://app.hypamoon.com/oauth/authorize`, token URL `https://api.hypamoon.com/oauth/token`, scopes: `sites:write`)

Puts a container on a site: every path of the site (and its own domain) then goes to it. Send a public image, or the source of a Node or Python app without a Dockerfile: we install the dependencies on Linux, scan them and build the image. The work runs after the answer (202): get_container shows when the version is live. A new version only gets traffic after its health_path answers 2xx; if it never does, the previous version keeps running. Settings left out on a new version are kept from the running one.

## Request body

- object
  - `site_id` string, uuid, required: The site that serves the container: every path of the site (and its own domain) goes to it. Create the site first with deploy_site.
  - `name` string
  - `image` string: A public linux/amd64 image, like ghcr.io/acme/api:1.4 or docker.io/library/nginx@sha256:... We copy it, so a later change upstream changes nothing here. Use image or source, not both.
  - `source` object: The app as files, without a Dockerfile: a Node app (package.json) or a Python app (requirements.txt or pyproject.toml). We install the dependencies on Linux and build the image.
    - `files` object[], required
      - `path` string, required: Path inside the app, like package.json, server.js or src/app.py. No node_modules or .venv: we install the dependencies.
      - `content` string, required: The file content: text, or base64 with encoding base64.
      - `encoding` 'utf8' | 'base64': utf8 (default) or base64 for binary files.
  - `runtime` 'node' | 'python': Only with source. Left out: package.json means node, requirements.txt or pyproject.toml means python.
  - `build_command` string: Only with source. Left out: npm run build when package.json has a build script, nothing for Python.
  - `start_command` string: Only with source. Left out: npm start (or node main), or the web line of a Procfile, or python main.py/app.py.
  - `port` integer: The port the server listens on. Default 8080; the container also gets it as PORT.
  - `instance_type` 'lite' | 'basic' | 'standard-1' | 'standard-2': Size: lite (1/16 vCPU, 256 MiB), basic (1/4 vCPU, 1 GiB), standard-1 (1/2 vCPU, 4 GiB), standard-2 (1 vCPU, 6 GiB). Default lite.
  - `instances` integer: How many instances share the traffic. Default 1.
  - `always_on` boolean: true keeps the instances running (no start-up wait, for webhooks and realtime). Default false: the container sleeps after sleep_after without traffic and starts on the next request.
  - `sleep_after` string: Idle time before sleeping, like "5m" or "1h". Default 5m. Ignored with always_on.
  - `health_path` string: A path that answers 2xx when the app is ready. A new version only gets traffic after this answers. Default /.
  - `env` object: Plain environment variables (not secret). Left out on a new version: the running version's env is kept.
  - `secrets` string[]: Vault secrets the container may use without ever seeing them: send a request with the header x-hypamoon-secret: <name> and we add the secret on the way out (the safe default).
  - `env_secrets` object[]: Vault secrets the code itself must know (a database URL, a signing key), as environment variables. The code can read and send these; prefer secrets when an API key in a header is enough.
    - `secret` string, required: The name of a secret in the project's vault (request_secret).
    - `as` string, required: The environment variable the container reads, like DATABASE_URL.
  - `allowed_hosts` string[]: The only hosts the container may call over HTTPS, like api.example-payments.com or *.example.com. api.hypamoon.com is always allowed. Nothing else gets out.

## Response `202`

The container and the deploy in the queue.

- object
  - `id` string, uuid, required
  - `site_id` string, uuid, required
  - `name` string, required
  - `url` string, required
  - `mode` 'live' | 'test', required
  - `status` 'deploying' | 'running' | 'failed' | 'suspended', required
  - `suspended` object
    - `reason` 'blocked' | 'site_offline' | 'plan' | 'balance', required
    - `since` string, required
    - `next_step` string, required
  - `version` integer, required: The live version; 0 until the first deploy is live.
  - `instance_type` string, required
  - `instances` integer, required
  - `always_on` boolean, required
  - `sleep_after` string, required
  - `port` integer, required
  - `health_path` string, required
  - `allowed_hosts` string[], required
  - `secrets` string[], required
  - `env_secrets` object[], required
    - `secret` string, required
    - `as` string, required
  - `env_names` string[], required: Names of the plain environment variables (never the values of secrets).
  - `created_at` string, required
  - `updated_at` string, required
  - `deploy` object, required
    - `version` integer, required
    - `status` 'queued' | 'deploying' | 'live' | 'previous' | 'failed', required: queued and deploying: still running; live: serving traffic; previous: an older live version; failed: see error.
    - `source` object, required
      - `kind` 'image' | 'build' | 'rollback' | 'settings', required
      - `image` string
      - `runtime` string
      - `from_version` integer
    - `created_at` string, required
    - `steps` object[]: How the rollout went, step by step.
      - `step` string, required
      - `ms` number, required
      - `ok` boolean, required
    - `error` object: Why it failed; log is the end of the build output.
      - `code` string, required
      - `message` string, required
      - `log` string
    - `build` object: For a build from source: the dependency scan. Known vulnerabilities are listed, not blocked; known malicious packages block the deploy.
      - `packages` integer, required
      - `vulnerable` object[], required
        - `package` string, required
        - `ids` string[], required
      - `scan_skipped` string
      - `seconds` number, required
  - `kept` string[]

## Other responses

- `400`: Bad Request: invalid_json.
- `401`: Unauthorized: unauthorized, missing_api_key, invalid_api_key, api_key_revoked, project_deleted.
- `402`: Payment Required: plan_limit_reached.
- `403`: Forbidden: project_not_enabled, key_mode_mismatch, missing_scope, verification_required, forbidden, site_offline.
- `404`: Not Found: not_found.
- `409`: Conflict: conflict, plan_limit_reached.
- `413`: Error: app_too_large.
- `422`: Unprocessable Content: validation_failed, app_blocked.
- `429`: Too Many Requests: rate_limited, deploy_limit_reached.
- `500`: Internal Server Error: internal_error.
- `503`: Service Unavailable: service_unavailable.

---

[API](https://skmtc.dev/hypamoon/apis/hypamoon-api.md) · [All operations](https://skmtc.dev/hypamoon/apis/hypamoon-api/llms.txt) · [OpenAPI document](https://skmtc.dev/hypamoon/apis/hypamoon-api/revisions/9f0b90004c06?raw)
