Skip to content

Services API

Services are deployable units inside projects. A service can be a Git source service, Docker image service, database service, worker, or static site.

Database service creation has its own page: Databases API.

All examples assume:

Terminal window
export AEROPLANE_URL="https://pilot.example.com"
export AEROPLANE_API_KEY="ap_..."
POST /api/projects/:projectId/services

Required access: write

Project scope: project must be visible to the key.

Payload:

{
"name": "web",
"repoFullName": "acme/web",
"branch": "main",
"rootDir": null,
"installCommand": "npm install",
"buildCommand": "npm run build",
"startCommand": "npm start",
"buildMethod": "auto",
"dockerfilePath": null,
"runtimeMode": "web",
"internalPort": 3000,
"env": [
{ "key": "NODE_ENV", "value": "production" }
]
}

buildMethod controls how the image is built:

  • auto (default) — if the repository contains a Dockerfile at its root, the deployment builds it with docker build; otherwise Railpack analyzes the project.
  • dockerfile — always build with the repository’s Dockerfile. The deployment fails if it is missing.
  • railpack — always build with Railpack, even when a Dockerfile is present.

dockerfilePath optionally points at a Dockerfile in a non-standard location, relative to the service root directory (for example docker/Dockerfile.web). The AEROPLANE_DOCKERFILE_PATH service environment variable takes precedence when set. Custom install, build, and start commands do not apply to Dockerfile builds.

Use repoFullName for GitHub repositories. Use repoUrl instead for a direct Git URL:

{
"name": "web",
"repoFullName": null,
"repoUrl": "https://github.com/acme/web.git",
"branch": "main",
"runtimeMode": "web",
"internalPort": 3000
}

Example:

Terminal window
curl -X POST "$AEROPLANE_URL/api/projects/project_123/services" \
-H "Authorization: Bearer $AEROPLANE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "web",
"repoFullName": "acme/web",
"branch": "main",
"runtimeMode": "web",
"internalPort": 3000,
"env": [
{ "key": "NODE_ENV", "value": "production" }
]
}'

Response:

{
"service": {
"id": "svc_web",
"projectId": "project_123",
"name": "web",
"slug": "web",
"repoFullName": "acme/web",
"repoUrl": "https://github.com/acme/web",
"dockerImage": null,
"branch": "main",
"rootDir": null,
"hasGithubToken": false,
"installCommand": null,
"buildCommand": null,
"startCommand": null,
"staticOutput": null,
"buildMethod": "auto",
"dockerfilePath": null,
"detectedBuildMethod": null,
"runtimeMode": "web",
"internalPort": 3000,
"hostPort": 41001,
"databasePublicEnabled": false,
"databasePublicHostname": null,
"postgresLogicalReplicationEnabled": false,
"status": "idle",
"reachable": false,
"localUrl": "http://127.0.0.1:41001",
"primaryUrl": "http://127.0.0.1:41001",
"preferredDomain": null,
"framework": null,
"lastDeployedAt": null,
"createdAt": "2026-06-10T08:40:00.000Z",
"updatedAt": "2026-06-10T08:40:00.000Z"
}
}
POST /api/projects/:projectId/services

Required access: write

Payload:

{
"name": "api",
"repoUrl": "docker-image",
"dockerImage": "ghcr.io/acme/api:latest",
"branch": "main",
"runtimeMode": "web",
"internalPort": 8080,
"env": [
{ "key": "NODE_ENV", "value": "production" }
]
}

Response:

{
"service": {
"id": "svc_api",
"projectId": "project_123",
"name": "api",
"slug": "api",
"repoFullName": "image:ghcr.io/acme/api:latest",
"repoUrl": "docker-image",
"dockerImage": "ghcr.io/acme/api:latest",
"branch": "main",
"rootDir": null,
"runtimeMode": "web",
"internalPort": 8080,
"hostPort": 41002,
"status": "idle",
"createdAt": "2026-06-10T08:42:00.000Z",
"updatedAt": "2026-06-10T08:42:00.000Z"
}
}
GET /api/services/:serviceId/overview

Required access: read

Project scope: service project must be visible to the key.

Example:

Terminal window
curl "$AEROPLANE_URL/api/services/svc_web/overview" \
-H "Authorization: Bearer $AEROPLANE_API_KEY"

Response:

{
"service": {
"id": "svc_web",
"projectId": "project_123",
"name": "web",
"status": "active",
"runtimeMode": "web",
"internalPort": 3000,
"hostPort": 41001,
"primaryUrl": "https://web.example.com"
},
"deployments": [
{
"id": "dep_123",
"serviceId": "svc_web",
"commitSha": "abc1234",
"status": "running",
"trigger": "manual",
"imageTag": "aeroplane-svc_web-dep_123",
"containerName": "aeroplane-svc_web-stable",
"startedAt": "2026-06-10T08:45:00.000Z",
"finishedAt": "2026-06-10T08:46:00.000Z",
"createdAt": "2026-06-10T08:45:00.000Z"
}
],
"env": [
{
"id": "env_123",
"key": "NODE_ENV",
"hasValue": true,
"value": "production",
"resolvedValue": "production",
"createdAt": "2026-06-10T08:40:00.000Z",
"updatedAt": "2026-06-10T08:40:00.000Z"
}
],
"domains": [
{
"id": "domain_123",
"serviceId": "svc_web",
"hostname": "web.example.com",
"status": "active",
"createdAt": "2026-06-10T08:41:00.000Z",
"updatedAt": "2026-06-10T08:41:00.000Z"
}
],
"publicIp": "203.0.113.10"
}
PATCH /api/services/:serviceId

Required access: write

Project scope: service project must be visible to the key.

Payload:

{
"branch": "main",
"rootDir": "apps/web",
"buildCommand": "npm run build",
"startCommand": "npm start",
"buildMethod": "auto",
"dockerfilePath": null,
"runtimeMode": "web",
"internalPort": 3000
}

detectedBuildMethod in service responses reports which builder the most recent deployment actually used (dockerfile or railpack); it is null until a deployment has run.

Response:

{
"service": {
"id": "svc_web",
"projectId": "project_123",
"name": "web",
"rootDir": "apps/web",
"buildCommand": "npm run build",
"startCommand": "npm start",
"runtimeMode": "web",
"internalPort": 3000,
"updatedAt": "2026-06-10T08:50:00.000Z"
}
}
POST /api/services/:serviceId/transfer

Required access: write

Project scope: key must access both the source project and the target project.

Payload:

{
"targetProjectId": "project_456"
}

Response:

{
"service": {
"id": "svc_web",
"projectId": "project_456",
"name": "web",
"slug": "web"
},
"project": {
"id": "project_456",
"name": "Internal",
"slug": "internal",
"serviceCount": 1,
"services": []
},
"caddy": {
"ok": true,
"detail": "Caddy reloaded"
}
}
DELETE /api/services/:serviceId

Required access: write

Project scope: service project must be visible to the key.

Example:

Terminal window
curl -X DELETE "$AEROPLANE_URL/api/services/svc_web" \
-H "Authorization: Bearer $AEROPLANE_API_KEY"

Response:

{
"ok": true,
"caddy": {
"ok": true,
"detail": "Caddy reloaded"
}
}
GET /api/services/:serviceId/suggestion-keys

Required access: read

Response:

{
"suggestions": [
{
"key": "hostPort",
"label": "Local service hostPort"
},
{
"key": "postgres-db.POSTGRES_URL",
"label": "Service postgres-db variable"
}
],
"databaseVariables": [
{
"key": "POSTGRES_URL",
"value": "${postgres-db.POSTGRES_URL}",
"label": "PostgreSQL private URL"
}
]
}