Cron jobs
Not every process is a long-running server. Sometimes you need a task that runs on a schedule — a nightly database sync, a weekly report, a cleanup job. Launchfile handles this with two fields: schedule and restart: no.
schedule is a standard cron expression (midnight daily). restart: no tells the provider this is a one-shot task, not a daemon.name: daily-sync
runtime: node
schedule: "0 0 * * *"
restart: "no"
commands:
start: "node scripts/sync.js" version: launch/v1
name: daily-sync
runtime: node
schedule: "0 0 * * *"
restart: "no"
requires:
- type: postgres
set_env:
DATABASE_URL: $url
commands:
start: "node scripts/sync.js" Cron jobs don't have provides: — they're not servers, they don't listen on ports, and nothing connects to them. They just run, do their work, and exit.
Images, builds, and runtimes
Lesson 1 introduced two paths: runtime + commands.start for building from source, or image for pulling a prebuilt container. There's a third field, build, for Dockerfile-based builds — and these three can coexist.
Each answers a different question:
runtime:— what language is this? Metadata only; tooling and buildpack-style providers use it.build:— how do I build from source? Dockerfile path, context, build args, and build-time secrets.image:— what's the image called? Either the image you pull, or the tag of whatbuildproduces.
The build and image fields mirror Docker Compose:
imagealone → pull the image from a registry.buildalone → build from a Dockerfile; the provider names the artifact.build+image→ build from source and tag the result asimage(useful when you want to push to a registry).runtime+ anything →runtimeis metadata; it doesn't change how the container is made.
Lesson 5 distinguished commands.build (shell commands inside an already-built container) from the top-level build: block (Dockerfile-based image builds). They're different layers: commands.build runs inside the container, build: creates the container.
Platform pinning
Many third-party images only ship for linux/amd64. The platform field tells the provider which OCI architecture to pull — important on ARM hosts (Apple Silicon dev machines, Graviton, Raspberry Pi) so the right variant is fetched or emulation is arranged.
version: launch/v1
name: hedgedoc-backend
image: ghcr.io/hedgedoc/hedgedoc/backend:develop
platform: linux/amd64
provides:
- protocol: http
port: 3000
bind: "0.0.0.0"
requires:
- type: postgres
set_env:
HD_DATABASE_URL: $url
health:
path: /api/private/config
start_period: 60s image:- A specific version tag, not :latest — prebuilt deploys should be reproducible.
platform:- Declares the target architecture. This image only ships amd64; providers on ARM hosts need to know so they can emulate or refuse.
requires:- Prebuilt images declare dependencies the same way as source-based apps — the provider injects env vars before starting the container.
health:- Health checks apply to prebuilt images too. start_period gives the container time to initialize before checks begin.
Host access
Some apps need direct access to the host machine — CI runners that manage Docker containers, deployment tools that need the Docker socket, monitoring agents that read host metrics. The host field declares these requirements explicitly.
There are three host access types: host.docker for Docker daemon access, host.network for sharing the host network stack, and host.filesystem for reading or writing host paths.
Add host.privileged: true when the app needs elevated privileges — e.g. device access for monitoring agents or kernel-level tools. Providers treat this as the most sensitive host flag and may refuse it in managed environments.
Host access breaks the container isolation boundary. Providers may restrict or refuse apps that request it. Always document why your app needs host access in the Launchfile's description.
Here's a deployment tool that needs all three:
version: launch/v1
name: launchpad
description: DevOps agent — give it a GitHub URL, get a running app
runtime: bun
host:
docker: required # Needs Docker daemon (not Docker-in-Docker)
network: host # Shares host network to manage container ports
filesystem: read-write # Persistent state in ~/.launchpad/
requires:
- type: docker
description: Orchestrates app containers on the host
provides:
- protocol: http
port: 3001
exposed: true
env:
ANTHROPIC_API_KEY:
required: true
LAUNCHPAD_HOME:
default: ~/.launchpad
commands:
install: bun install
build: bun run build
start: bun run src/server.ts
health: /api/health host:- Declares exactly what host-level access this app requires.
docker: required- Needs the real Docker daemon — Docker-in-Docker won't work.
network: host- Shares the host network to manage container ports directly.
filesystem: read-write- Persistent state stored on the host filesystem.
Optional dependencies
You've been using requires: for dependencies your app can't run without. But some dependencies are optional — a Redis cache that improves performance but isn't mandatory, or an S3 bucket for file uploads that falls back to local storage.
Use supports: for these. The provider provisions them if available, but the app still starts without them.
requires:
- type: postgres
set_env:
DATABASE_URL: $url supports:
- type: redis
set_env:
REDIS_URL: $url Your app should check whether the environment variable is set and gracefully degrade when it isn't. The Launchfile just declares the intent — your code handles the fallback.
In the wild
Uptime Kuma is a self-hosted monitoring tool. It's one of the simplest prebuilt-image patterns in the catalog — just an image, a port, persistent storage, and a health check.
version: launch/v1
name: uptime-kuma
description: "Self-hosted monitoring tool"
repository: https://github.com/louislam/uptime-kuma
logo: https://raw.githubusercontent.com/louislam/uptime-kuma/master/public/icon.svg
image: louislam/uptime-kuma:1
provides:
- protocol: http
port: 3001
exposed: true
health: /
storage:
data:
path: /app/data
persistent: true
restart: always image:- Pulls the official image from Docker Hub — no source build needed.
storage:- Persistent storage for monitoring data. Survives container restarts.
restart: always- Monitoring tools should always be running — restart on crash.
Try it: npx launchfile up uptime-kuma to launch this app locally.
View in catalog
Check your understanding
- Cron jobs use
schedule+restart: no— noprovidesneeded. runtime,build, andimagedescribe three different things and can coexist:runtimeis metadata,buildcreates the image,imagenames it. Useplatform:to pin OCI architecture.- Host access (
host.docker,host.network,host.filesystem) is security-sensitive — declare it explicitly. supports:is likerequires:but optional — the app gracefully degrades without it.
Course complete!
You're ready to write Launchfiles for any application.