Update service
PATCHhttps://api.instacloud.com/projects/{projectId}/services/{serviceId}
Token scope: account · org · project — read-only tokens refused (not a GET).
Apply several service settings at once (compute and managed databases): rename, always-on, resource ceiling and data volume size. The whole patch is validated before anything applies, and every machine-facing change lands as one reconfigure per machine (one restart, not one per field).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
projectId | path | string | yes | |
serviceId | path | string | yes |
Request body
application/json
| Field | Type | Required | Description |
|---|---|---|---|
name | string | no | new service name (lower-kebab, max 39); applied last so the other fields in this request still address the service by its current name |
alwaysOn | boolean | no | true = machines never autostop; false = scale-to-zero (the platform default for new compute services; refused on a worker) |
memoryMb | integer | no | memory ceiling in MB — a multiple of 256, within the per-vCPU band |
cpu | integer | no | vCPU ceiling; requires memoryMb, derived from it when omitted |
startCommand | string | no | Compute runtime startup override via sh -c, including source-built images; overrides the built image CMD on next deploy or restart. Empty resets to image default. Persisted as readable service settings; use secrets for credentials. |
volumeMountPath | string | no | Compute only: container mount path chosen on attachment; defaults to /data. Requires volumeGib only when attaching. Existing path changes are staged until the next deploy. |
volumeGib | integer | no | the persistent volume size in whole Gi — attaches when none exists, grows an existing one (never shrinks) |
Responses
200
Default Response
| Field | Type | Required | Description |
|---|---|---|---|
service | object | no | A project-level service (postgres | storage | compute | redis | mysql | mongodb). |
service.id | string | no | |
service.project_id | string | no | |
service.type | "postgres" | "storage" | "compute" | "redis" | "mysql" | "mongodb" | no | |
service.name | string | no | |
service.spec | object | no | compute: {cpu_kind,cpus,memory_mb}; postgres: sized by its instance ceiling (cpuMilli/memoryMib) |
service.machine_count | integer | no | compute only |
service.desired_state | "running" | "stopped" | "suspended" | no | compute only — developer-set lifecycle intent |
service.always_on | boolean | no | compute 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.websocket | boolean | null | no | compute 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" | null | no | compute 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_host | string | null | no | compute, 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.region | string | null | no | |
service.domain | string | null | no | default access domain (postgres/compute) |
service.public | boolean | no | storage — 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_host | string | null | no | compute 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_fqdn | string | null | no | compute 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.image | string | null | no | compute only — container image currently run |
service.port | integer | null | no | compute/managed database — compute image port, or the private database TCP port |
service.start_command | string | null | no | Runtime 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_path | string | null | no | Last successfully confirmed Compute mount path; null means unknown, not proof of a changed path. |
service.volume_mount_path | string | null | no | Compute volume mount path; null or omitted means /data for legacy volumes. Changes apply on deployment. |
service.volume_gib | integer | null | no | compute/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_version | integer | null | no | postgres 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_id | string | null | no | the template deployment that created this service (survives branch clones); null for services created directly |
service.template_modified | boolean | no | true once the service was reconfigured away from its template-deployed spec |
service.template_code | string | null | no | the template this service was deployed from; null for services created directly, and for one whose deployment record has been pruned |
service.template_logo_url | string | null | no | the 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_name | string | null | no | the 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_version | string | null | no | the 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_version | string | null | no | the 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_available | boolean | no | true 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.source | object | no | compute 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.owner | string | no | |
service.source.repo | string | no | |
service.source.branch | string | no | the repo branch this service tracks |
service.source.auto_deploy | boolean | no | a push to `branch` rebuilds and redeploys the service |
service.status | "creating" | "active" | "error" | "deleting" | "deleted" | no | |
service.created_at | string | no | |
applied | string[] | yes | |
limits | object | no | |
limits.cpu | integer | yes | |
limits.memoryMb | integer | yes | |
cap | object | no | |
cap.cpu | integer | yes | |
cap.memoryMb | integer | yes | |
cap.volumeGib | integer | yes | |
volume | object | no | |
volume.sizeGib | integer | yes | |
volume.mountPath | string | yes | |
volume.id | string | null | no | |
volume.appliedMountPath | string | null | no | |
volume.pending | boolean | no | |
volumeAttached | boolean | no | true when this patch attached the volume (it mounts on the next deploy) |
202
Default Response
| Field | Type | Required | Description |
|---|---|---|---|
status | "approval_required" | no | |
approvalId | string | no | |
action | string | no | the gated action, or a comma-joined compound set — prefer `actions` |
actions | string[] | no | every capability this approval covers |
message | string | no | |
url | string | no | the console page where a project admin reviews this request |
nextActions | object[] | no | |
nextActions.op | string | yes | Neutral 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.reason | string | yes | Natural-language, human/LLM-facing "why do this now". |
nextActions.args | object | no | Suggested, flat named arguments; "<placeholder>"s where a value is unknown. |
nextActions.gated | boolean | no | True if the action passes a governance gate. |
400
Default Response
| Field | Type | Required | Description |
|---|---|---|---|
error | string | yes |
403
Default Response
| Field | Type | Required | Description |
|---|---|---|---|
error | string | yes |
404
Default Response
| Field | Type | Required | Description |
|---|---|---|---|
error | string | yes |
409
a rename to a taken name; or code private_name_taken: another service in the branch already derives the same private network name
| Field | Type | Required | Description |
|---|---|---|---|
error | string | yes | |
code | string | no | stable machine-readable reason, e.g. private_lane_disabled, private_lane_unavailable_in_region, public_access_closed, credential_change_in_progress |
502
Default Response
| Field | Type | Required | Description |
|---|---|---|---|
error | string | yes |