Documentation menu

Add service

POSThttps://api.instacloud.com/projects/{projectId}/services

Token scope: account · org · project — read-only tokens refused (not a GET).

Add a service on demand. Assigns a default access domain (postgres/compute). Gated — service.add.

Parameters

NameInTypeRequiredDescription
projectIdpathstringyes

Request body

application/json

FieldTypeRequiredDescription
type"postgres" | "storage" | "compute" | "redis" | "mysql" | "mongodb"yes
namestringyeslower-kebab: a-z, 0-9, hyphen
branchstringnotarget branch (default: project default branch)
region"us-east" | "eu-central" | "ap-southeast"noregion for postgres/compute/managed databases (see GET /regions); invalid for storage
publicbooleannostorage only — provision the bucket with anonymous public-read (default false)
imagestringnocompute only — container image to run at creation (e.g. nginx:1.27)
portintegernocompute only — port the image listens on (default 8080)
endpoint"http" | "tcp" | "udp" | "tcp+udp"nocompute only — how the service is reached. http (default): HTTPS at the service domain. tcp / udp / tcp+udp: raw TCP, UDP, or both on `port` at the service’s own public IPv6 address (IPv6 clients only, no platform TLS, no custom domain). These need an explicit port. tcp refuses ports that are commonly scanned (21, 22, 23, 25, 53, 445, 3389), web ports (80, 443) or reserved by the platform; udp refuses known amplification ports (7, 17, 19, 53, 111, 123, 137, 138, 161, 162, 389, 520, 1900, 3702, 5353, 11211) and ports reserved by the platform; tcp+udp refuses both lists. Available on the insta-compute provider only.
alwaysOnbooleannocompute and databases (postgres, redis, mysql, mongodb) — idle mode at creation; not for storage. Omitted: the platform default per kind. Compute: INSTA_COMPUTE_ALWAYS_ON_DEFAULT (false on InstaCloud: scale-to-zero — a machine with no inbound or outbound traffic suspends and wakes on the next request). Databases: INSTA_DB_ALWAYS_ON_DEFAULT (true on InstaCloud: the database stays warm, so an app waking from scale-to-zero never waits on a database cold start too; false opts a new database into scale-to-zero — the idle instance suspends and its first connection after idling cold-starts). true = always-on: never scales to zero. A worker (port 0) is always-on regardless; false with port 0 is refused. For postgres the value is recorded on the database instance (GET /projects/{projectId}/database/instance, scaleToZero) and changed with PATCH .../database/settings; for the other types on the service row (always_on) and changed with PUT .../services/{serviceId}/always-on. All plans; billing is actual usage either way — an always-on database bills its resident memory while idle.
volumeMountPathstringnoCompute only: container mount path chosen on attachment; defaults to /data. Requires volumeGib only when attaching. Existing path changes are staged until the next deploy.
volumeGibintegernocompute only — attach a persistent volume of this many whole Gi (attachable later too via PUT /services/:id/volume; the volume never detaches, but DELETE /services/:id/volume destroys it, data and all). Any plan may create up to the configured free volume cap (10Gi by default, env-overridable per deployment); a larger size is paid and capped at the org's own plan cap (50Gi on paid plans by default), like growing it later. Constraints: machineCount stays 1, and scale-to-zero uses stop (cold wake) instead of suspend — both lift if the volume is deleted.
pgVersionintegernopostgres only — the Postgres major version to create the database on: 16, 17, 18. Omitted: the platform default (INSTA_DB_POSTGRES_MAJOR; 16 on InstaCloud). Fixed for the database's lifetime; branches keep it. 501 when the database provider cannot create that major yet.
sourceobjectno
source.type"github"no
source.ownerstringyes
source.repostringyes
source.installationIdintegernoGitHub App installation id; required unless public
source.repoIdintegernoGitHub repository id; required unless public
source.publicbooleannoa public repo, connected without an App installation: one-shot deploys, auto_deploy is always false
source.branchstringnothe repo branch to track; default: the repo’s default branch
source.rootDirstring | nullnosubdirectory to build from (monorepo); relative, no leading / or ..; null/omitted = repo root. It also decides which pushes deploy this service: the build context is this directory, so a push that changed nothing under it is skipped
source.autoDeploybooleannorebuild on every push to `branch` (default true; must be false or omitted for a public repo)
source.buildCommandstring | nullno
source.startCommandstring | nullno
source.watchPathsstring[] | nullnoAn include list of gitignore patterns, matched against paths RELATIVE TO THE REPOSITORY ROOT rather than to `rootDir` — e.g. ["apps/web/**", "packages/ui/**"]. A push deploys this service only when it changed a matching path; omit or null to fall back to the root-directory rule (a service with `rootDir` deploys when a push changed something under it, one building from the repo root deploys on every push). `!` narrows the list under git's own rule that a path cannot be re-included once an earlier pattern took its directory, so ["apps/web/**", "!**/*.md"] drops apps/web/README.md but not apps/web/docs/guide.md. Inert on a public repo, which cannot auto-deploy at all
source.portinteger | nullnothe port the app listens on; written to the service

Responses

201

Default Response

FieldTypeRequiredDescription
serviceobjectnoA project-level service (postgres | storage | compute | redis | mysql | mongodb).
service.idstringno
service.project_idstringno
service.type"postgres" | "storage" | "compute" | "redis" | "mysql" | "mongodb"no
service.namestringno
service.specobjectnocompute: {cpu_kind,cpus,memory_mb}; postgres: sized by its instance ceiling (cpuMilli/memoryMib)
service.machine_countintegernocompute only
service.desired_state"running" | "stopped" | "suspended"nocompute only — developer-set lifecycle intent
service.always_onbooleannocompute and managed databases (redis, mysql, mongodb) — true: machines never scale to zero; false: idle machines suspend and wake on request. A new compute service is born false (scale-to-zero, except a worker); a new managed database is born from INSTA_DB_ALWAYS_ON_DEFAULT (true on InstaCloud). Postgres is not recorded here: its idle mode lives on the database instance (GET /projects/{projectId}/database/instance, scaleToZero). Billing is actual usage either way.
service.websocketboolean | nullnocompute only — the app speaks WebSocket, so its machines carry connections-based concurrency and a 512 MB guest floor. Re-asserted by any redeploy given no flag, including restart. null: never recorded.
service.endpoint"http" | "tcp" | "udp" | "tcp+udp" | nullnocompute only — how the service is reached. http: Host-routed HTTPS at `domain`. tcp / udp / tcp+udp: raw TCP, UDP, or both at `endpoint_host`:`port` on the service’s own public IPv6 address (IPv6 clients only; no TLS from the platform, no custom domain, no WebSocket flag). Chosen at create, and changed by a deploy that passes `endpoint`. null: never chosen, which is http.
service.endpoint_hoststring | nullnocompute, endpoint tcp/udp/tcp+udp only — the host to reach on `port` (an IPv6-only name), set once a deploy has confirmed it; null otherwise
service.regionstring | nullno
service.domainstring | nullnodefault access domain (postgres/compute)
service.publicbooleannostorage — bucket(s) served with anonymous public-read; managed databases — the database port is exposed on a public address (on insta-compute, false = reachable only over the private network, *_PRIVATE_URL); postgres — the public endpoint accepts connections (true by default; false = not reachable from the internet, private network lane only); compute — the service answers the internet at its URL / endpoint_host (true by default; false = the URL answers 403 and never wakes it, and only services in the same branch reach it, at private_host). Change it with PUT .../services/{serviceId}/access
service.private_hoststring | nullnocompute and managed databases on insta-compute — the name other services in the SAME project branch reach this one at, on `port` over TCP: `<name>.insta.internal` for compute, `<name>-<type>.insta.internal` for a managed database (redis/mysql/mongodb). Other branches and projects cannot reach it. Resolves through the search domain every service is started with; tools that skip the search list (nslookup, dig) need private_fqdn. null: the service has no private address (a worker, a name ending in '-', a service not yet back-filled, or a region without the private network)
service.private_fqdnstring | nullnocompute and managed databases on insta-compute — the fully-qualified form of private_host, `<private name>.<network>.insta.internal`, for clients that do not use the search domain. null whenever private_host is
service.imagestring | nullnocompute only — container image currently run
service.portinteger | nullnocompute/managed database — compute image port, or the private database TCP port
service.start_commandstring | nullnoRuntime startup override, run via sh -c; on insta-compute it runs in place of the image's ENTRYPOINT and CMD: a wrapper entrypoint (e.g. docker-entrypoint.sh) does not run while it is set. null restores the image's own ENTRYPOINT and CMD. Stored as readable service settings.
service.volume_applied_mount_pathstring | nullnoLast successfully confirmed Compute mount path; null means unknown, not proof of a changed path.
service.volume_mount_pathstring | nullnoCompute volume mount path; null or omitted means /data for legacy volumes. Changes apply on deployment.
service.volume_gibinteger | nullnocompute/managed database — size (whole Gi) of the persistent data volume; null = no volume. Compute can attach/grow via PUT .../volume; managed databases are platform-managed in v1
service.pg_versioninteger | nullnopostgres only — the Postgres MAJOR version the service runs (e.g. 16), known before the instance is ever connected to or woken. Pick client tooling (pg_dump/pg_restore/psql) of the same major. null: never recorded (a legacy row). The exact server_version of a RUNNING instance is on GET .../database/metrics
service.template_deployment_idstring | nullnothe template deployment that created this service (survives branch clones); null for services created directly
service.template_modifiedbooleannotrue once the service was reconfigured away from its template-deployed spec
service.template_codestring | nullnothe template this service was deployed from; null for services created directly, and for one whose deployment record has been pruned
service.template_logo_urlstring | nullnothe template's mark; null when the template is not in the registry (or carries no logo). Official templates: a commit-pinned jsDelivr URL (TEMPLATE_LOGO_HOST). Community templates: any https:// link the author set. Joined live rather than stored, because a republish moves it
service.template_namestring | nullnothe template's name as the registry spells it, for a surface people read; null when the template is not in the registry, or its manifest omits a name. Read from the manifest the registry serves NOW, beside the logo, because a republish can rename it
service.template_versionstring | nullnothe template version this instance was deployed from; null for a service created directly. Read from the deployment record, so it does not move when the registry is republished
service.template_latest_versionstring | nullnothe version the registry serves for this template now; null when the registry does not serve it — absent, unpublished or taken down. The row still reports template_code and template_name in those cases, because attribution of a resource its owner deployed is not catalog visibility. For display beside template_version, never for deciding whether an update exists
service.template_update_availablebooleannotrue when upgrading this service's template deployment to the version the registry serves now would actually change something about the services it runs: a setting the new manifest would write, an environment variable it would write differently, a service it would add, or a variable it now requires that this instance holds no value for. A republish that only moved presentation is not an update, because applying it would change nothing, and neither is a new version string on identical content, which is what every community republish produces. False whenever the answer cannot be determined: a template absent from the registry, unpublished, taken down, a manifest this version of the platform cannot parse, or a registry read that failed. False too while the deployment's own run has not succeeded, because what an unfinished deploy needs is a retry, not an upgrade
service.sourceobjectnocompute only — where the running image comes from: an image somebody deployed (type=image) or a connected repository (type=github). Full resource: GET /projects/{id}/services/{serviceId}/source
service.source.type"image" | "github"yes
service.source.ownerstringno
service.source.repostringno
service.source.branchstringnothe repo branch this service tracks
service.source.auto_deploybooleannoa push to `branch` rebuilds and redeploys the service
service.status"creating" | "active" | "error" | "deleting" | "deleted"no
service.created_atstringno
sourceobjectnoWhere a compute service’s running image comes from: an image somebody deployed (type=image, the absence of a connected source) or a connected repository (type=github).
source.type"image" | "github"yes
source.imagestring | nullnotype=image — the image the service runs; null when nothing has been deployed yet
source.idstringno
source.ownerstringno
source.repostringno
source.branchstringnothe repo branch this service tracks; defaults to the repo’s default branch
source.root_dirstring | nullnosubdirectory to build from (monorepo); null = repo root. Also decides which pushes deploy this service: the build context is this directory, so a push that changed nothing under it is skipped
source.watch_pathsstring[] | nullnoinclude list of gitignore patterns, matched against paths relative to the repository root (not to root_dir); a push deploys this service only when it changed a matching path. null/absent falls back to the root-directory rule: a service with root_dir deploys when a push changed something under it, one building from the repo root deploys on every push
source.auto_deploybooleannoa push to `branch` rebuilds and redeploys; always false for a public repo (no App, no webhooks)
source.publicbooleannoconnected as a public repo, without a GitHub App installation
source.installation_idstring | nullno
source.repo_idstring | nullnoGitHub repository id (bigint as string)
source.build_commandstring | nullnocustom build command for source builds without a Dockerfile; null uses automatic detection
source.start_commandstring | nullno
source.created_atstringno
source.updated_atstringno
source.last_buildobject | nullnothe newest build of this service (null before the first)
source.last_build.idstringno
source.last_build.source_idstring | nullnothe service source that queued it; null once that source was disconnected
source.last_build.project_idstringno
source.last_build.service_idstring | nullno
source.last_build.branch_idstring | nullno
source.last_build.commit_shastringno
source.last_build.status"queued" | "fetching" | "building" | "deploying" | "live" | "failed"no
source.last_build.image_refstring | nullno
source.last_build.errorstring | nullno
source.last_build.created_atstringno
source.last_build.updated_atstringno
buildobjectno
build.buildIdstringyes
build.queuedbooleanyesfalse when this commit already has a build (dedup)
build.serviceIdstringyes
nextActionsobject[]no
nextActions.opstringyesNeutral logical action id, e.g. "service.add" — NOT an operationId or a CLI/MCP tool name; each client maps it to its own surface.
nextActions.reasonstringyesNatural-language, human/LLM-facing "why do this now".
nextActions.argsobjectnoSuggested, flat named arguments; "<placeholder>"s where a value is unknown.
nextActions.gatedbooleannoTrue if the action passes a governance gate.

202

Default Response

FieldTypeRequiredDescription
status"approval_required"no
approvalIdstringno
actionstringnothe gated action, or a comma-joined compound set — prefer `actions`
actionsstring[]noevery capability this approval covers
messagestringno
urlstringnothe console page where a project admin reviews this request
nextActionsobject[]no
nextActions.opstringyesNeutral logical action id, e.g. "service.add" — NOT an operationId or a CLI/MCP tool name; each client maps it to its own surface.
nextActions.reasonstringyesNatural-language, human/LLM-facing "why do this now".
nextActions.argsobjectnoSuggested, flat named arguments; "<placeholder>"s where a value is unknown.
nextActions.gatedbooleannoTrue if the action passes a governance gate.

400

Default Response

FieldTypeRequiredDescription
errorstringyes

403

Default Response

FieldTypeRequiredDescription
errorstringyes

409

the name is taken; or code private_name_taken: another service in the branch already derives the same private network name (a compute cache-redis beside a redis cache)

FieldTypeRequiredDescription
errorstringyes
codestringnostable machine-readable reason, e.g. private_lane_disabled, private_lane_unavailable_in_region, public_access_closed, credential_change_in_progress

429

Default Response

FieldTypeRequiredDescription
errorstringyes

501

the database provider cannot create the requested pgVersion yet — nothing was created

FieldTypeRequiredDescription
errorstringyes

502

the provider could not provision the service — nothing was created; safe to retry

FieldTypeRequiredDescription
errorstringyes

View as Markdown