To start your application you have to define a container. Let's call it CuOS Init App; it runs as cuos-app. Bringing your own is the Own CuOS Init App level — if you do not, you get CuOS IaC.
What you can do within this container:
- Start your application
- Start more docker container (you need to mount the docker socket, see app_command label)
- Interact with the CuOS API via
/var/run/cuos.sock.
Relevant configuration fields, see system.json:
{
"init_image": "your-registry/your-app",
"init_image_version": "1.2.3",
"init_image_digest": "sha256:..."
}CuOS IaC is a CuOS Init App like any
other — the one you get if you do not bring your own.
cuos-release's release.json pins it as init_image, so a configuration that
includes release.json already has one and starts CuOS IaC, which then
deploys your services from a git repository.
Setting init_image yourself replaces it:
{
"#include": ["cuos-release/release.json"],
"init_image": "your-registry/your-app",
"init_image_version": "1.2.3",
"init_image_digest": "sha256:..."
}The system's name follows the same key: product_name defaults to CuOS IaC
when init_image contains cuos-iac, and to CuOS otherwise
(product_name in updater/lib/config.sh).
FROM your-base-image
# Your application setup
COPY app /app
If the application image sets a label:
LABEL dev.cuos.app_command="--env FOO=bar --cap-add NET_ADMIN"Its value replaces the default Docker run argument block (still augmented with standard names/restart/logging flags). Keep security in mind: avoid unnecessary capabilities.
Default:
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume /root/.docker/config.json:/root/.docker/config.json:ro \
--volume /system.json:/system.json:ro \
--volume /var/run/cuos.sock:/var/run/cuos.sock \
--volume /usr/local/share/ca-certificates/custom:/usr/local/share/ca-certificates/custom:roSuggestions for more parameters:
--memory 512MB \
--user myuser:mygroup \
--read-only \
--tmpfs /etc/ssl/certs:rw,noexec,nosuid,size=16m
--tmpfs /tmp:rw,size=64mTo make ca-certificate import work, you have to execute
update-ca-certificates --freshAdditional runtime environment variables, that are set by CuOS:
| Variable | Purpose |
|---|---|
SYSTEM_TYPE=cuos |
Identify environment |
SYSTEM_CONFIG_PATH=/system.json |
Location of active config |
VIRT_TYPE |
Result of systemd-detect-virt |
| Positional Args | Current + previous app version (used by your entrypoint logic) |
CuOS runs scripts inside the container by name. All of them are optional — if the file is not there, CuOS carries on.
| Script | When | Called by |
|---|---|---|
/api/post_update |
after a successful OS/app update | system/cuos/app-init.sh:195, exit code ignored |
/api/trigger |
cuos trigger, with the input on stdin and the arguments passed through |
system/cuos/api.sh:344 |
/api/cuos-trigger-update |
cuos trigger-update; if it is missing, CuOS runs cuos update itself |
system/cuos/api.sh:149 |
post_update is where migrations or internal reconfiguration belong — the one
place guaranteed to run once, after the new version is in place.
cuos-trigger-update is the one hook whose exit code matters. Exit 0 tells
CuOS your app has taken the update over; on 1 or 126 CuOS runs cuos update
itself. So do not leave an empty stub there: it returns 0 and
cuos trigger-update then does nothing at all. CuOS IaC's
implementation
wakes its poll loop, which does the work.
Don't forget to add execution permission to your scripts.
What a hook is given:
| Name | Type | Description |
|---|---|---|
SYSTEM_TYPE |
env | Always cuos for identification |
SYSTEM_CONFIG_PATH |
env | Path to active config (/system.json) |
VIRT_TYPE |
env | Result of systemd-detect-virt (e.g. kvm, docker, none) |
$1 |
arg | Current application version (from init_image_version) |
$2 |
arg | Previous application version (if available) |
| Failure Scenario | Host Reaction |
|---|---|
| App fails to start repeatedly during update window | Triggers OS rollback |
| Digest never matches (tamper?) | Continuous pull attempts + error logs |
| Timeout (> 3600s startup/update) | Rollback & reboot |
| Single transient pull failure | Retry every 60s |
Rollback resets to previous OS subvolume and replays last stable config.
- Use digests for production to ensure integrity.
- Store persistent writable data under mounted
/datavolumes if you add any (consider explicit mounts rather than relying on default host path sharing beyond what CuOS provides). - Avoid
--privilegedunless mandatory; prefer granular--cap-add.
[systemd] → cuos-app.service → app-init.sh
└─ waits for Docker daemon ready
└─ performs registry login (if configured)
└─ evaluates desired vs current image digest
├─ pull if missing / version change / digest mismatch
├─ create or start container `cuos-app`
└─ invoke optional in-container hook (/api/post_update)