Apps & Services
An app is the configuration you manage; the containers it runs are the services. This page covers how they relate, the settings an app carries, and what sh0 does by default.
Apps vs Services
- App: the persistent configuration. Its source, settings, environment variables, domains and resource limits survive every deployment.
- Service: a container created by one deployment, named
sh0-<stack-id-prefix>-<app>-<deployment-id-prefix>. Each deployment creates a new one; the previous container is stopped and removed only once the new one is healthy and routed.
App Status
An app shows one of these statuses:
| State | Meaning |
|---|---|
| inactive | Created, never deployed. |
| building | A deployment is building the image or starting the container. |
| running | The container passed its health check and receives traffic. |
| stopped | Stopped by a user (sh0 stop or the dashboard). |
| failed | The last deployment failed. A container from an earlier deployment that was still running keeps serving. |
Creating an App
In a stack, Add App opens the Deploy page with the stack preselected. An app can come from a Docker image, a Git repository, an uploaded archive, a Dockerfile, a Docker Compose file, a framework starter, or the template catalog. From a terminal, sh0 push uploads the current directory and deploys it.
App Settings
The app's Settings tab holds these fields, saved with Save Changes:
| Field | Description |
|---|---|
| App Name | Identifier of the app; also its hostname inside the stack (<name> and <name>.internal). |
| Repository URL, Branch | For Git apps: the repository and the branch to deploy. |
| Build Path, Dockerfile | Sub-directory to build from, and the Dockerfile to use. Without a Dockerfile, sh0 detects the stack and generates one. |
| Port | Port the app listens on inside the container; public traffic is routed to it. |
| Replicas, Start order, Project | Number of containers, position when the whole stack is deployed, and the stack the app belongs to. |
There are no build-command or start-command fields: the Dockerfile decides. Build arguments are environment variables marked as build-time, see Environment Variables.
Resource Limits
Every app container has a CPU and memory limit, on every installation, with or without a license:
| Field | Default | Allowed values |
|---|---|---|
| CPU | Half the cores of the host, at least 0.5 | cpu_limit in millicores (1000 = one core), from 100 to the cores of the host |
| Memory | 512 MB | memory_limit in MB, from 64 to the memory of the host |
Change them in the app's Scaling tab, under Resource Limits, then Save Configuration. Through the API, both values are integers and 0 resets a value to its default. A string such as "512m" is refused with 422.
$ curl -s -X PUT https://panel.example.com/api/v1/apps/e17d4314-ff61-485a-a509-58b3974c1eee/resources \
-H "Authorization: Bearer $SH0_TOKEN" -H "Content-Type: application/json" \
-d '{"cpu_limit":500,"memory_limit":256}'
{"data":{"app_id":"e17d4314-ff61-485a-a509-58b3974c1eee","autoscale_cpu_threshold":80,"autoscale_enabled":false,"autoscale_memory_threshold":80,"cooldown_seconds":300,"cpu_limit":500,"created_at":"2026-10-02 02:52:11","current_replicas":1,"id":"64433fb7-a676-4257-8c78-474d2fb961fd","last_scale_at":null,"lb_policy":"round_robin","max_replicas":1,"memory_limit":256,"min_replicas":1,"updated_at":"2026-10-02 03:04:09"}}New limits apply at the next deployment; the running container keeps the old ones until then. Observed on a test app: before the redeployment the container still had 512 MB and 2 cores (a 4-core host), after it 256 MB and 0.5 core:
$ docker inspect <container> --format '{{.HostConfig.Memory}} {{.HostConfig.NanoCpus}}'
536870912 2000000000
$ sh0 deploy my-api
[*] Deploying my-api...
Deployment ID: 9b98d35b-0751-490e-88e8-f089fb0a6007
Status: queued
$ docker inspect <new-container> --format '{{.HostConfig.Memory}} {{.HostConfig.NanoCpus}}'
268435456 500000000Restart Policy
Every app container runs with the Docker policy unless-stopped: Docker restarts it after a crash or a reboot of the host, unless you stopped it. The policy is not configurable.
Deployment Health Check
Before routing traffic to a new container, sh0 waits until it is healthy, for up to 300 seconds. The check is chosen automatically and has no settings:
- If the image defines a Docker
HEALTHCHECK, sh0 uses it. - Otherwise, for a web app, a TCP connection to the app port must succeed. There is no HTTP path check.
- A worker (no port) is healthy once it has stayed running for 15 seconds without restarting.