---
title: "Deploys and versions"
description: "Every deploy of an app makes a version, which takes the traffic once it is built and ready. What a version is made of, what happens when one fails, and how to roll back."
---

> Documentation Index
> Fetch the complete documentation index at: https://glac.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploys and versions

An app is made with `POST /v1/apps`, or with `glac deploy`, which makes it when the project has none of that name. With an `image`, its first version is deployed at the same time. Each later deploy is `POST /v1/apps/{app}/versions`, or `glac deploy` again with the same `--name`.

```bash
glac deploy ghcr.io/acme/api:1.4 --name api --port 8080 --ready-http /healthz
glac deploy ghcr.io/acme/api:1.5 --name api        # the next version of the same app
```

## What a version is

A version is made of what changes how the app runs:

- the image, pinned to its digest at deploy time (a tag that later points elsewhere does not change the version);
- the start command or its arguments, the port and the ready check;
- environment variables, secrets, and the secret that pulls a private image;
- the size, the volumes and Drives, the files written before it starts, the user, the working directory, how it is stopped;
- its kind (`service`, which runs for as long as it is there, or `job`, which runs to its end) and its host name;
- its runtime settings (`runtime`, below).

Changing one of these deploys a new version. A version is never changed after it is made. Deploying the same tag again makes a new version too, since the tag may point at another image by then.

A version's `resolved` says what the deploy found of the image, once: the image by its digest, the port its instances are asked on, and `unmounted_volumes`, the paths the image declares as volumes (`VOLUME`) that no volume or Drive of the version is mounted at. Docker would mount a volume of its own there; here such a path is a directory of each instance's own disk, which a new version does not keep, and `glac deploy` warns of it.

Other settings take effect at once and make no version: the scale, the regions, keys, network rules, labels, log settings, whether it is public, and the name.

| Field | When you leave it out |
|---|---|
| `port` | The one port the image declares, else 8080. |
| `ready` | The port takes a TCP connection. Or `{"http": "/path"}` (answers 200 to 399), or `{"command": …}` (succeeds in the instance). |
| `size` | 2 vCPUs and 4 GiB (the Free plan's largest), and never more than your plan allows. |
| `regions` | All three: `na`, `eu`, `ap`. |
| `start_command` | The image's own `ENTRYPOINT` and `CMD`. |

### Runtime settings

A version's `runtime` sets what a Compose file's `ulimits`, `sysctls`, `tmpfs`, `dns` and `shm_size` ask for. The instance's machine applies it as it boots, before its template is saved, and the limits hold in the start command and in every command run in the instance:

- `ulimits`: limits by Docker's names (`nofile`, `nproc`, `memlock`, `stack`, …), a number for both the soft and the hard limit or `{soft, hard}`; -1 is none. Open files from 1 to 1,048,576. Without one, open files are 1,024, raised to 524,288 at the most, as under Docker.
- `sysctls`: the kernel settings Docker lets a container set: `net.*`, `kernel.shm*`, `kernel.msg*`, `kernel.sem`, `fs.mqueue.*`. Any other is refused (400, naming it), and one the machine's kernel does not have fails the version's build.
- `tmpfs`: up to 16 file systems in the instance's memory, each `{path, size_bytes, mode}` (half the memory, and mode 1777, unless you say); not over the machine's own (`/proc`, `/sys`, `/dev`, `/run`), the platform's, or a volume's path.
- `dns`: its `/etc/resolv.conf`, `{servers, search, options}`, in place of the image's. On the project's private network the platform answers its questions in the servers' place, the project's names first.
- `shm_size_bytes`: the size of `/dev/shm`; half the memory unless you say.

```bash
glac deploy postgres:16 --name db --ulimit nofile=65535 --sysctl net.core.somaxconn=1024 --shm-size 1g
glac cube update db --reset runtime        # none again
```

Any of `--ulimit`, `--sysctl`, `--tmpfs /scratch:size=64m,mode=1770`, `--dns`, `--dns-search`, `--dns-option` and `--shm-size` replaces the whole of the version's runtime settings; the SDKs take `runtime`, and the MCP server's `cube_deploy` too. A sandbox takes the same `runtime` when it is made ([making a sandbox](/docs/sandbox/commands/#making-a-sandbox)).

`glac cube update api -e KEY=value` sets one variable and keeps the others. With the API and the SDKs, `env` and `secrets` given to a deploy are all the version has.

## How a deploy goes

The platform pulls the image, starts it on the machines of each region the app runs in, waits for its ready check, and saves the running service as a template. Instances are copies of that template, so the service is already running in each one.

A version is `building`, then `ready` or `failed`. When it is ready it takes the app's traffic: requests in progress on the old version are answered, new ones go to the new version, and no request is lost. A version that does not come up is `failed`, with the image's last words in its `error`, and the traffic stays where it was. A machine with no room left on its disk for the version is passed over for another of its region that has room; where none has, the version fails at once (`drive_full`, with how full the machine's disk is). A version is never left `building` on a machine that takes nothing of it: after two minutes it fails (`deploy_stalled`), with what the machine said. `glac deploy` waits for the version and exits 1 when it fails; `--no-wait` returns at once.

```bash
glac cube versions api       # newest first: building, ready or failed, and which has the traffic
```

## Rolling back

`POST /v1/apps/{app}/rollback` with `{"version": …}` (a number or an id), or `glac cube rollback api 3`, sends the traffic to another version that is `ready`, older or newer. Without a version, the command line picks the newest ready version before the current one.

The machines keep the templates of the version that has the traffic, of any being built, and of the three newest versions that are ready (the platform's number, the same for every app), so a rollback to one of them moves the traffic at once. An older version whose template was cleared is built again first, and the traffic moves when it is ready; that is waited for as a deploy is. Every version stays in `glac workers versions`, and any of them that was ready can be rolled back to.

## Deleting an app

`glac cube rm api` (`DELETE /v1/apps/{app}`) frees its name. Requests in progress are answered for up to 30 seconds more, WebSockets are closed, and then its instances are deleted. Its logs can still be read, by its id, for as long as the plan keeps logs.

## Limits

An account has at most so many apps in all its projects (past it, 403 `app_limit_reached`), an app at most so many environment variables and secrets together (403 `env_limit_reached`), and an account makes at most so many apps and versions an hour (403 `plan_cap_reached`, with `Retry-After`):

|  | Free | Paid |
|---|---|---|
| Cubes | 10 | 500 |
| Environment variables and secrets of one app | 64 | 128 |
| Cubes and versions made an hour | 10 | 500 |

See [limits](/docs/platform/limits/) for the rest.

Source: https://glac.ai/docs/cube/versions/index.mdx
