---
title: "PUT /api/v1/hosting/apps/{app_id}/services/{id}"
method: PUT
path: "/api/v1/hosting/apps/{app_id}/services/{id}"
tags: ["hosting"]
---

# PUT /api/v1/hosting/apps/{app_id}/services/{id}

`PUT /api/v1/hosting/apps/{app_id}/services/{id}`

## Request

- Base URL: `https://composal.com` (the document declares no server; this is the origin it was published from)
- URL: `https://composal.com/api/v1/hosting/apps/{app_id}/services/{id}`
- Auth: HTTP bearer

## Path parameters

- `app_id` string, required
- `id` string, required

## Request body

- AppServiceUpdatePayload
  - `slug` string
  - `name` string
  - `kind` 'web' | 'worker' | 'cron' | 'static' | 'private'
  - `runtime` 'docker' | 'node' | 'ruby' | 'python' | 'go' | 'rust' | 'static' | 'generic'
  - `runtime_adapter_kind` 'nomad' | 'database'
  - `runtime_adapter_settings_json` string, nullable
  - `nomad_namespace` string, nullable
  - `nomad_region` string, nullable
  - `cpu_mhz` integer, nullable
  - `memory_mb` integer, nullable
  - `memory_max_mb` integer, nullable: Hard memory limit in MB the task may burst to (Nomad MemoryMaxMB). Must be >= memory_mb, which stays the scheduler reservation. Memory above the reservation is oversubscribed and best effort. Null clears the override.
  - `disk_mb` integer, nullable
  - `nomad_constraints_json` string, nullable
  - `nomad_update_strategy_json` string, nullable
  - `nomad_ulimits_json` string, nullable: Docker task ulimits as a JSON object of rlimit name to "soft:hard" (e.g. {"nofile":"65536:65536"}). Null/absent leaves the container on the daemon default.
  - `nomad_kill_timeout_seconds` integer, nullable: Seconds the task gets after SIGTERM before SIGKILL. Null clears the override and leaves Nomad's 5s default. Capped at 30 because a Nomad client silently clamps kill_timeout to its own max_kill_timeout (default 30s).
  - `nomad_shutdown_delay_seconds` integer, nullable: Seconds Nomad waits between deregistering the group's services and sending SIGTERM, so the edge stops routing new requests to the allocation before it is signalled. Null clears the override.
  - `source_path` string
  - `build_command` string, nullable
  - `release_command` string, nullable
  - `prestart_command` string, nullable
  - `start_command` string, nullable
  - `healthcheck_path` string, nullable
  - `schedule` string, nullable
  - `internal_port` integer, nullable
  - `desired_instances` integer
  - `publicly_accessible` boolean
  - `auto_deploy` boolean
  - `deploy_paths` string[]: Repo-root-relative path prefixes/globs a push must touch for the service to auto-deploy. Empty array clears the override (default to app root_path).

## Response `200`

Updated app service

- object
  - `service` AppService, required
    - `id` string, required
    - `hosted_app_id` string, required
    - `slug` string, required
    - `name` string, required
    - `kind` 'web' | 'worker' | 'cron' | 'static' | 'private', required
    - `runtime` 'docker' | 'node' | 'ruby' | 'python' | 'go' | 'rust' | 'static' | 'generic', required
    - `runtime_adapter_kind` 'nomad' | 'database', required
    - `runtime_adapter_settings_json` string, nullable, required
    - `nomad_namespace` string, nullable, required
    - `nomad_region` string, nullable, required
    - `cpu_mhz` integer, nullable, required
    - `memory_mb` integer, nullable, required
    - `memory_max_mb` integer, nullable, required: Hard memory limit in MB the task may burst to (Nomad MemoryMaxMB). memory_mb stays the reservation the scheduler places against, so raising this does not make a rollout harder to place. Must be >= memory_mb. Memory above the reservation is oversubscribed and best effort: under node pressure the kernel reclaims it and can OOM-kill the task. Null/absent renders no limit beyond the reservation.
    - `disk_mb` integer, nullable, required
    - `nomad_constraints_json` string, nullable, required
    - `nomad_update_strategy_json` string, nullable, required
    - `nomad_ulimits_json` string, nullable, required: Docker task ulimits as a JSON object of rlimit name to "soft:hard" (e.g. {"nofile":"65536:65536"}). Null/absent leaves the container on the daemon default.
    - `nomad_kill_timeout_seconds` integer, nullable, required: Seconds the task gets after SIGTERM before SIGKILL. Null/absent leaves Nomad's 5s default. Capped at 30 because a Nomad client silently clamps kill_timeout to its own max_kill_timeout (default 30s).
    - `nomad_shutdown_delay_seconds` integer, nullable, required: Seconds Nomad waits between deregistering the group's services and sending SIGTERM, so the edge stops routing new requests to the allocation before it is signalled. Null/absent leaves Nomad's 0s default.
    - `source_path` string, required
    - `repo_source_path` string, required
    - `build_command` string, nullable, required
    - `release_command` string, nullable, required
    - `prestart_command` string, nullable, required
    - `start_command` string, nullable, required
    - `healthcheck_path` string, nullable, required
    - `schedule` string, nullable, required
    - `internal_port` integer, nullable, required
    - `desired_instances` integer, required
    - `publicly_accessible` boolean, required
    - `auto_deploy` boolean, required
    - `deploy_paths` string[], required: Explicit repo-root-relative path prefixes/globs a push must touch to auto-deploy. Empty means default to the app root_path.
    - `effective_deploy_paths` string[], required: Tracked paths actually applied by the auto-deploy trigger (explicit deploy_paths, else [root_path]).
    - `bindings` AppServiceBinding[], required
      - `id` string, required
      - `hosted_app_id` string, required
      - `app_service_id` string, required
      - `bindable_type` string, required
      - `bindable_id` string, required
      - `binding_type` string, required
      - `name` string, required
      - `mount_path` string, nullable, required
      - `read_only` boolean, required
      - `required` boolean, required
      - `created_at` string, date-time, required
      - `updated_at` string, date-time, required
    - `created_at` string, date-time, required
    - `updated_at` string, date-time, required
    - `created_at_label` string, nullable, required
    - `updated_at_label` string, nullable, required
    - `cpu_label` string, nullable, required
    - `memory_label` string, nullable, required
    - `memory_max_label` string, nullable, required
    - `disk_label` string, nullable, required

## Other responses

- `401`: Unauthorized
- `403`: Forbidden
- `404`: Not found
- `422`: Deployment request failed validation
- `502`: Bad gateway

---

[API](https://skmtc.dev/composal/apis/vex-api.md) - [All operations](https://skmtc.dev/composal/apis/vex-api/llms.txt) - [OpenAPI document](https://skmtc.dev/composal/apis/vex-api/revisions/19698ba051dc?raw)
