Create a service and deploy it.
API key access: full.
apiKeyAuthorizationBearer <pyo_…>An API key from the dashboard's API keys page: Authorization: Bearer pyo_…. Keys can expire, and an expired one gets a 401.
application/jsonCreate a service and deploy it.
API key access: full.
op*"createService""createService"service*The service, as in the snapshot's services: id (svc_…), projectId, clusterId, name, source, ports, env and the rest.
A template's service records where it came from in source: templateVersion, appVersion, images (each Compose service's image, repository:tag@digest, by name) and fields (the deploy form's values, with a secret one only as {"secret": true, "set": …, "env": …}). Saves keep them. Responses add customized when its image is no longer the template's.
Its ports: name, containerPort, protocol ("HTTP", "TCP" or "UDP"), exposure ("internal", "http" for a website, "nodeport", or "loadbalancer" for a public TCP or UDP port) and publicPort.
A public TCP or UDP port goes through the gateway unless proxy is false. Then, for TCP only, it can have ipFilter ({"mode": "allow" or "deny", "cidrs": [...]}), connectionLimit (open connections on the port, from everyone together), sendProxyProtocol ("V1" or "V2", to the service) and acceptProxyProtocol (read the visitor's address from a header sent by a load balancer in front, while connections without one still work). Ports 22, 53, 80, 443 and a few others can't be public. Each public port belongs to one service on its cluster.
Its healthCheck, which says when a replica is ready: type ("http" for a request to path on port answered with 2xx or 3xx, "tcp" for port accepting connections, "exec" to run command, or "none" so deploys don't wait for it), initialDelaySeconds and periodSeconds. With auto ("default" or "static") Pyo picks it from what each build finds: a static site or single-page app gets an HTTP check of /. A check without auto is kept as it is.
A Git source has buildArgs ([{"key": …, "value": …}]), used by its next build: Dockerfile build arguments, or Railpack settings such as RAILPACK_BUILD_CMD. They can end up in the image, so no secrets.
deploymentId*stringA new id you choose for its first deployment: dpl_ then 4 to 40 lowercase letters, digits, dashes or underscores.
volumes?array<>Volumes to create with it.
databases?array<>Databases to connect it to before its first deploy, as connectDatabase does: each {"id": "mdb_…", "env": {"WORDPRESS_DB_HOST": "MYSQL_HOST", …}} (env optional).
confirm?booleanGo ahead with a very large replica change: without it, one that goes to 20 or more replicas and to more than 10 times what the service had, or more than twice what its servers have room for, is refused with 409 and needsConfirm: true. Ask the user before sending it.
Done. Always ok: true, and some operations add more, such as a new API key or a join command. A warning comes with a change that went through but won't fully take effect yet, such as more replicas than the servers have room for.
application/jsoncurl -X POST 'https://your-pyo.example.com/api/v1/ops' \
-H 'Content-Type: application/json' \
-d '{
"deploymentId": "dpl_8kq2x7ad",
"op": "createService",
"service": {
"clusterId": "cl_8kq2x7ad",
"domains": [],
"env": [],
"id": "svc_8kq2x7ad",
"name": "web",
"ports": [
{
"containerPort": 80,
"exposure": "http",
"name": "http",
"protocol": "TCP"
},
{
"containerPort": 6379,
"exposure": "loadbalancer",
"ipFilter": {
"cidrs": [
"203.0.113.0/24"
],
"mode": "allow"
},
"name": "redis",
"protocol": "TCP",
"proxy": true,
"publicPort": 6379
}
],
"projectId": "prj_8kq2x7ad",
"replicas": 2,
"source": {
"image": "nginx:1.29",
"type": "image"
}
}
}'{}