Docs / Core Concepts / Apps & Services

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:

StateMeaning
inactiveCreated, never deployed.
buildingA deployment is building the image or starting the container.
runningThe container passed its health check and receives traffic.
stoppedStopped by a user (sh0 stop or the dashboard).
failedThe 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:

FieldDescription
App NameIdentifier of the app; also its hostname inside the stack (<name> and <name>.internal).
Repository URL, BranchFor Git apps: the repository and the branch to deploy.
Build Path, DockerfileSub-directory to build from, and the Dockerfile to use. Without a Dockerfile, sh0 detects the stack and generates one.
PortPort the app listens on inside the container; public traffic is routed to it.
Replicas, Start order, ProjectNumber 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:

FieldDefaultAllowed values
CPUHalf the cores of the host, at least 0.5cpu_limit in millicores (1000 = one core), from 100 to the cores of the host
Memory512 MBmemory_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.

Terminal
$ 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:

Terminal
$ 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 500000000
Warning
A container that exceeds its memory limit is killed by the kernel (OOMKilled). If that happens during a deployment, the deployment fails at once instead of waiting for the health-check timeout. Raise the limit or reduce the app's memory use.

Restart 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.
Note
A crash loop or an out-of-memory kill fails the deployment immediately. When a deployment fails, the previous container is not stopped and keeps serving traffic.