Skip to content

Application YAML (v3)

The canonical Application format used by edgible stack deploy. One YAML document describes one application; multiple documents in a single file are separated by ---.

The schema is enforced by the control plane and the agent. Unknown keys are rejected.

apiVersion: v3
kind: Application
metadata: { ... }
spec: { ... }
FieldRequiredTypeNotes
apiVersionyesv3The only supported value today.
kindyesApplicationThe only supported value today.
metadatayesobjectSee Metadata.
specyesobjectSee Spec.
metadata:
name: my-app
organization: <org-id>
labels:
env: production
annotations:
owner: platform-team
dependsOn: [other-app-name]
FieldRequiredTypeNotes
nameyesstringUnique within the organization. Lowercase alphanumeric with hyphens, 1–63 chars.
organizationyesstringYour organization ID. Find with edgible config list.
labelsnomapFree-form key/value strings. Used by placement.deviceSelector.labels.
annotationsnomapFree-form key/value strings. Not interpreted by the platform.
dependsOnnostring[]Names of other applications that must be deployed before this one.
spec:
placement: { ... }
workloads: [ ... ]
storage: [ ... ] # optional
access: [ ... ] # optional but typical
cloud: { ... } # optional, only valid when placement.strategy is cloud or automatic

spec.workloads is the only required field. At least one workload must be declared.

Where the application runs. Three strategies, with different required fields.

# Run on a specific device you own
placement:
strategy: serving-device
deviceSelector:
deviceName: my-first
migrationPolicy: manual
# Run on Edgible-managed cloud infrastructure
placement:
strategy: cloud
region: us-east-1
# Let the platform pick (cloud or device based on capabilities)
placement:
strategy: automatic
FieldRequiredTypeNotes
strategyyesenumserving-device, cloud, or automatic.
deviceSelectorrequired for serving-deviceobjectSee below.
regionrequired for cloudstringEdgible cloud region (e.g., us-east-1).
migrationPolicynoenummanual (default), allowed, or automatic. Governs whether the application can be moved between devices. See Migrate between devices.

At least one selector field is required.

FieldTypeNotes
deviceNamestringPin to a device by name.
deviceIdstringPin to a device by ID.
labelsmapMatch devices that carry these labels.
regionstringMatch devices in this region.
capabilitiesstring[]Match devices that report these capabilities (e.g., gpu, kvm).

A list of one or more workloads. Common fields apply to every type:

workloads:
- name: api
type: compose | docker | managed-process | vm | pre-existing
ports: [ ... ]
storage: [ ... ] # optional
env: { ... } # optional
envFrom: [ ... ] # optional
healthChecks: [ ... ] # optional
resources: { ... } # optional
restartPolicy: always # optional
dependsOn: [ ... ] # optional, names of other workloads
FieldRequiredTypeNotes
nameyesstringUnique within the application.
typeyesenumOne of compose, docker, managed-process, vm, pre-existing.
portsusuallyarrayPorts the workload listens on. Required if access[] references this workload.
storagenoarrayVolumes from spec.storage[] to mount.
envnomapString-to-string environment variables.
envFromnoarrayEnvironment from secret references. See envFrom.
healthChecksnoarraySee Health checks.
resourcesnoobjectCPU/memory requests and limits. See Resources.
restartPolicynoenumalways, on-failure, or never.
dependsOnnostring[]Names of other workloads in this application that must start first.
- name: api
type: compose
composeFile: ./api.compose.yml
projectName: api # optional
FieldRequiredTypeNotes
composeFileyesstringPath to a Docker Compose file relative to the application YAML, OR base64:<encoded yaml> (the agent decodes it before invoking compose).
projectNamenostringOverride the compose project name. Defaults to the workload name.

A single image, no compose file.

- name: redis
type: docker
image: redis:7-alpine
command: ["redis-server"]
args: ["--appendonly", "yes"]
FieldRequiredTypeNotes
imageyesstringA pullable image reference.
commandnostring[]Override the image’s ENTRYPOINT.
argsnostring[]Override the image’s CMD.

A long-running native process supervised by the agent.

- name: worker
type: managed-process
command: "./bin/worker --queue jobs"
workingDir: /opt/app
logFile: /var/log/edgible/worker.log
FieldRequiredTypeNotes
commandyesstringCommand line. The agent splits on whitespace; no shell expansion.
workingDirnostringCWD for the process. Defaults to the agent’s working directory.
logFilenostringWhere stdout/stderr are written.

A virtual machine. The disk can be declared inline (legacy) or via a storage[] mount (recommended; required for migration).

# Recommended: storage-backed disk
- name: legacy
type: vm
vmBackend: qemu
memory: 2048
cpus: 2
storage:
- { name: vm-disk, mountPath: /disk }
# Legacy: inline disk image path on the host
- name: legacy
type: vm
vmBackend: qemu
diskImage: /var/lib/edgible/vms/legacy.qcow2
memory: 2048
cpus: 2
FieldRequiredTypeNotes
vmBackendyesenumqemu, wsl, or firecracker.
memoryyesnumberMiB.
cpusyesnumberVirtual CPUs.
diskImageone ofstringHost path to a disk image. Mutually exclusive with a storage[] disk.
storage[]one ofarrayA storage mount referencing spec.storage[]. The first mount is the disk.
vmArchnoenumx86_64 or aarch64.
sshPortnonumberSSH port to expose to the agent.
sshPublicKeynostringPublic key to inject.
vmExtraArgsnostring[]Extra args passed to the backend.

Tells Edgible to forward to a process or service that’s already running on the device — useful for adopting an existing service without changing how it runs.

- name: legacy-svc
type: pre-existing
hostPort: 8080
hostAddress: 127.0.0.1 # optional, defaults to localhost
FieldRequiredTypeNotes
hostPortnonumberA port already listening on the device. Required unless the workload only declares ports[] for access targeting.
hostAddressnostringAddress the existing service listens on. Defaults to 127.0.0.1.
ports:
- { name: http, containerPort: 8080, protocol: tcp }
- { name: dns, containerPort: 53, protocol: udp }
FieldRequiredTypeNotes
nameyesstringUsed by access entries and health checks to reference this port.
containerPortyesnumberThe port number the workload listens on.
protocolnoenumtcp (default) or udp.

Mounts a volume declared in spec.storage[].

storage:
- { name: pgdata, mountPath: /var/lib/postgresql/data }
- { name: cache, mountPath: /tmp/cache, readOnly: false }
FieldRequiredTypeNotes
nameyesstringMust match a spec.storage[].name.
mountPathyesstringAbsolute path inside the workload.
readOnlynoboolMount read-only.
healthChecks:
- { type: http, port: http, path: /healthz, intervalSeconds: 10 }
- { type: tcp, port: db, intervalSeconds: 30 }
- { type: exec, command: ["pg_isready"], intervalSeconds: 60 }
FieldRequiredTypeNotes
typeyesenumhttp, tcp, or exec.
porthttp/tcpstringPort name from ports[].
pathhttpstringURL path to GET.
commandexecstring[]Command to run inside the workload.
intervalSecondsnonumberHow often to probe.
timeoutSecondsnonumberProbe timeout.
failureThresholdnonumberConsecutive failures before the workload is marked unhealthy.
resources:
requests:
cpu: "500m"
memory: 512Mi
limits:
cpu: "2"
memory: 2Gi
FieldTypeNotes
requests.cpu / limits.cpustringCores or millicores (e.g., 2, 500m).
requests.memory / limits.memorystringSize string (e.g., 512Mi, 2Gi).

Honored by compose, docker, and vm workloads where the backend supports it.

Inject environment variables from a secret stored in the platform.

envFrom:
- name: DATABASE_URL
secretRef: { secret: prod-db, key: url }
- name: API_TOKEN
secretRef: { secret: prod-api } # whole secret as the value
FieldRequiredTypeNotes
nameyesstringEnv var name to set inside the workload.
secretRef.secretyesstringSecret identifier in the platform.
secretRef.keynostringField within the secret.

Application-level storage volumes. Each entry is a logical volume that one or more workloads can mount.

storage:
- name: pgdata
type: persistent
size: 20Gi
mobility: movable
backup:
enabled: true
scheduleCron: "0 3 * * *"
retainCount: 7
encryption:
enabled: true
keyRef: { secret: storage-key }
FieldRequiredTypeNotes
nameyesstringReferenced by workloads[].storage[].name.
typeyesenumpersistent or ephemeral.
sizerequired for persistentstringSize like 20Gi, 500Mi.
drivernostringStorage driver hint. Most users leave unset.
mobilitynoenumimmovable (default), movable, replicated, or cloud-only. See below.
backupnoobject{ enabled, scheduleCron, retainCount }.
encryptionnoobject{ enabled, keyRef }.

Mobility values:

  • immovable — bound to the device it was created on; cannot migrate.
  • movable — the volume can be quiesced, snapshotted, transferred, and restored on another device. Required for Migrate between devices.
  • replicated — the volume is kept consistent on more than one device.
  • cloud-only — only valid with placement.strategy: cloud or automatic. Rejected on serving-device.

Public-facing entry points. Each entry exposes a workload port through Edgible’s edge.

access:
# HTTPS with a generated edgible.app hostname
- name: public
type: https
target: { workload: web, port: http }
hostname: { generated: true }
tls: { managedBy: edgible }
publish: true
policies:
auth: { mode: none }
# HTTPS with one or more custom domains
- name: api
type: https
target: { workload: api, port: http }
hostname: { custom: api.example.com }
tls: { managedBy: edgible }
policies:
auth: { mode: api-key }
ipRules:
allow: ["10.0.0.0/8", "192.168.0.0/16"]
rateLimit:
requestsPerMinute: 600
burst: 100
# Raw TCP
- name: postgres
type: tcp
target: { workload: db, port: pg }
listenPort: 5432
# Raw UDP
- name: dns
type: udp
target: { workload: dns, port: dns }
listenPort: 53
FieldRequiredTypeNotes
nameyesstringUnique within the application.
typeyesenumhttps, tcp, or udp.
target.workloadyesstringA name from spec.workloads[].
target.portyesstringA port name from that workload.
hostnamehttps onlyobject{ generated: true } OR { custom: <fqdn> } OR { custom: [<fqdn>, ...] }.
tlshttps onlyobject{ managedBy: edgible } (default) or { managedBy: passthrough }.
listenPorttcp/udp onlynumberPublic port the platform listens on.
publishnoboolDefaults to true. If false, the route is configured but not exposed publicly.
policiesnoobjectSee Policies.

Available on every access type, with tcp/udp supporting only ipRules and rateLimit.

FieldTypeNotes
auth.modeenumnone, edgible-login, api-key, or short-code. See Authentication modes.
auth.allowedOrganizationsstring[]When mode: edgible-login, restrict to specific orgs.
apiKey.requiredboolForce an API key in addition to the primary auth.mode.
shortCode.requiredboolForce a short code in addition to the primary auth.mode.
ipRules.allowstring[]CIDR allow-list.
ipRules.denystring[]CIDR deny-list.
rateLimit.requestsPerMinutenumberPer-source request budget.
rateLimit.burstnumberAllowed burst above the steady rate.
waf.enabledboolEnable web-application firewall.
waf.modeenumenforce or log-only.

Cloud-specific behavior. Only valid when placement.strategy is cloud or automatic. Rejected on serving-device.

cloud:
sleep:
enabled: true
idleAfter: 5m
wake: sync
minActive: 1
classes:
compute: standard
storage: ssd
isolation: dedicated
FieldTypeNotes
sleep.enabledboolSuspend the workload after it goes idle.
sleep.idleAfterdurationHow long to wait before sleeping (30s, 5m, 1h).
sleep.wakeenumsync (block the request while waking) or async (return immediately, wake in background).
minActivenumberMinimum active instances. 0 allows full sleep.
classes.compute / .storage / .isolationstringClass hints to the cloud scheduler.

See Cloud sleep and wake for tuning guidance.

You can put several Applications in one file, separated by ---:

apiVersion: v3
kind: Application
metadata:
name: db
organization: <org-id>
spec: { ... }
---
apiVersion: v3
kind: Application
metadata:
name: api
organization: <org-id>
dependsOn: [db]
spec: { ... }

edgible stack deploy -f all.yml deploys both, in dependsOn order. See Stack with dependencies for the full pattern.