Applications
An application is one or more container images, a private network, storage with a fixed size, and a domain with a certificate. It gets no Docker socket, no privileged mode and no host network.
What an application is
A site is a domain served by nginx from a directory. An application is a domain served by nginx from a container. Accounts, certificates, backups, jobs, resource limits and the reseller model work the same way on both.
- Service
- One image, running. An application may have several. They see each other by name on a network only they share.
- Route
- A domain, a service and a port inside that service. nginx and the certificate are KLYRN's, as for a site.
- Volume
- Storage that survives redeployment. Its own filesystem with its own fixed size.
- Release
- One deployment, recorded with the image digest it ran, so a rollback is exact.
Node.js applications have a different lifecycle and are documented under
Node.js. They are klyrn app. These are
klyrn container.
Creating and deploying
klyrn container list
klyrn container show shop
klyrn container deploy shop --image ghcr.io/you/shop:2.1
klyrn container route add shop shop.example.com --port 8080
klyrn container logs shop --tail 200 --search error
A deployment pulls the image, starts the new container and waits for it to answer. If it never answers, the deployment fails and the previous release is put back.
A service a domain reaches is checked before anyone can reach it. Its new container comes up on a private port beside the one that is serving. The health check is aimed at that port, and nginx moves to it only after the check passes. A database or worker, which nothing outside the server reaches, is replaced in place. A routed service that mounts a volume has two containers on it while the check runs. The full entry.
Every release records the digest it ran. A tag can move and a digest cannot, so a rollback puts back what was serving:
klyrn container releases shop
klyrn container rollback shop 7
A Docker Compose file can be imported from the panel. Unsafe fields are refused by name, with the reason. See what is refused.
What is enforced, and by what
A control the kernel applies is different from one a daemon reports. This table names what enforces each property.
| Property | Enforced by |
|---|---|
| A volume cannot exceed its size | ext4 on its own loop device. The write fails with ENOSPC. The server's free space does not move. |
| A container cannot write to its root filesystem | ReadonlyRootfs, in the kernel. The write is refused. |
| Scratch space is bounded | A tmpfs with a size, charged to the memory cgroup. |
| An account's memory and CPU | Its cgroup slice. |
| A container cannot regain dropped privileges | no-new-privileges, plus every capability dropped. |
| A container's writable layer | Nothing. See below. |
Why the root filesystem is read-only
Docker accepts a per-container storage limit. With the overlay driver it installs by default, it applies that limit to nothing. The limit is accepted at creation, as a daemon default and by the update endpoint. Each returns success and enforces nothing, so a container can fill the server's disk.
KLYRN therefore offers no writable-layer quota. It removes the writable layer:
the root filesystem is read-only by default, scratch space is a
tmpfs with a size, and anything that must survive goes in a volume. The
kernel applies all three.
A service can be given a writable root deliberately. KLYRN then states that what it writes outside its volumes is limited only by the server's free disk. The machine's disk reserve is not a limit on that application.
Volumes
A KLYRN volume is a sparse ext4 image on its own loop device: a real filesystem with a real size.
- Default size
- 1 GiB. The largest is 1 TiB.
- Filling it
- Stops that application and nothing else. The write fails, the server's free space does not move, and no other customer notices.
- Growing it
- Online, from the panel. It never shrinks.
- Backups
- Included unless the volume is marked excluded.
- Occupancy
- Measured from the filesystem and shown as used-of-capacity, with a warning before it fills.
An application will not start against an empty directory where its data should be. If a volume cannot be mounted, the deployment is refused and no container is created. A database that starts on an empty disk would initialise a new one.
The server's own reserve
KLYRN keeps headroom on the host that applications may not allocate into. It is 10% of the disk, never less than 2 GiB and never more than 20 GiB. A volume that would eat into it is refused when it is asked for.
Isolation
Every application gets a private network. Its services reach each other by name on it, and nothing else is on it.
- No Docker socket
- Mounting
/var/run/docker.sock(or containerd's, or CRI-O's, or Podman's) is refused. A container that can talk to the daemon can start a privileged one, which makes it root on the server. - No privileged mode
- Refused. A privileged container holds every capability and every host device. It is a root process with a different filesystem.
- No host networking
- Refused. It would put the container on the server's own network stack. There it could bind the server's ports and reach anything listening on
127.0.0.1, including the database and the panel, unfiltered by the firewall. - No host paths
- A container mounts KLYRN volumes, and nothing else off the host.
- Capabilities
- All dropped, always, with only a small reviewed set addable.
- Registries
- An allow-list:
klyrn container registry list | allow | deny. Allowing one is an administrator's decision.
The validated type KLYRN deploys from has no field for privileged, network mode, devices, host ports or host mounts. A future code path that forgets to check still cannot ask the daemon for one. The request type carries them only so they can be refused by name.
When something is refused
A refusal names the field, the value it held, and the reason, and explains what the field does.
The fix is almost always to delete the line. A container that wanted the Docker socket wanted to deploy things. A container that wanted host networking wanted a port. KLYRN gives it a route and a domain.
Environment and secrets
klyrn container env shop APP_ENV=production
klyrn container env shop --secret DATABASE_URL
klyrn container env shop --remove OLD_KEY
A secret's value is read from standard input, never from an argument.
An argument is visible in ps to every user on the machine, and it lands in
the shell history of whoever typed it.
Sealed values are stored sealed and unsealed at the last moment, by the deploy engine alone. They do not appear in the job log, a backup manifest, the panel or a support bundle.
Backups
klyrn container backup create shop
klyrn container backup list shop
klyrn container backup restore 128
klyrn container backup restore-as-new 128 --name shop-copy --domain copy.example.com
klyrn container backup schedule shop daily --keep 7
A backup holds the application's definition, the image digest each service ran, its routes, its limits, its environment including sealed secrets, and its volumes. It does not hold the image's layers. A restore pulls the recorded digest back from the registry.
A backup is enough to rebuild the application after it has been deleted, and it outlives the application on purpose.
Crash-consistent
A volume backup is crash-consistent. The filesystem is frozen and copied, and nothing asks the application to flush first. A database restored from one recovers the way it recovers from a power cut. That is reliable, and it is not identical to a clean shutdown.
restore-as-new builds a second application beside the original from the
same backup. It needs a domain of its own, because the original is still serving its
name.
Restarts and reboots
KLYRN remounts volumes and brings services back itself, in an order where nothing starts against storage that is not there yet. This is tested by rebooting a server with a volume 99% full and a WordPress site running beside the application.
A service that has died is shown as dead, whatever the page showed when it last loaded.
What this server can run
klyrn container capabilities
klyrn containers status
klyrn containers preflight
capabilities answers whether this server can run an application at all and
what it has left. preflight is the check the installer runs before enabling
the runtime, and it can be run by hand at any time.
The container runtime is optional. A KLYRN server that hosts only websites never installs it, and enabling it later is one operation.
Building from a git repository
An application can start from a repository with a Dockerfile. KLYRN clones it, builds the image on this server, and deploys it by the digest it built, never by a tag. After the image, it is the same engine as every other application: network, volumes, environment, health check, domain, certificate, backups, Move and rollback.
Applications → New application → Git repository
klyrn container source <name> [--repo URL] [--branch NAME] [--dockerfile PATH] [--context DIR]
klyrn container deploy <name> # clone, build, deploy, health-check, keep or roll back
klyrn container releases <name> # commit, branch, digest, how long the build took, what happened
klyrn container rollback <name> <release> # the image that release ran, started again; nothing is rebuilt
A build is treated as hostile code. It runs with no Docker socket, no
host network and no privileges. It has a hard memory limit with no swap behind it, a
CPU quota and a time limit. It is held to the same storage reserve every website on
the server depends on, so it cannot fill the disk. The build context is confined to
the repository. ../, absolute paths, symlinks that point out, devices and
FIFOs are refused before a byte reaches the daemon.
Failures are named. A build that fails says Build failed and production is unchanged. It never says a deployment failed when none started. A build that succeeds and then fails its health check leaves the previous release serving, and the message says which of the two happened.
Private repositories use a deploy key KLYRN generates per application. The panel shows the public half to paste into the repository as a read-only key. The private half never leaves the server. A push can deploy automatically through a signed webhook whose secret is shown exactly once. Auto-deploy is off until you turn it on, so a repository never deploys before its environment is set.