App
Use an app unit when you need to build a container and run a long-lived service.
type: appimage: python:3.13-slim
builds: - files: ["requirements.txt"] script: | pip install --no-cache-dir -r requirements.txt
- files: ["*"] script: | python manage.py collectstatic --noinput
runtime: port: 8000 env: ALLOWED_HOSTS: "*" DB_NAME: ${app-db:name} DB_USER: ${app-db:user} DB_PASSWORD: ${app-db:password} DB_HOST: ${app-db:host} DB_PORT: ${app-db:port} API_TOKEN: ${secret:api-token} init: | python manage.py migrate --noinput cmd: "gunicorn app.wsgi:application --bind 0.0.0.0:8000"
exports: - source: /app/staticfiles path: /static
timers: - name: cleanup schedule: "0 3 * * *" script: | python manage.py cleanupFields
Section titled “Fields”| Field | Required | Purpose |
|---|---|---|
image |
Yes | Base image used for the build. |
builds |
Yes | Ordered build layers. |
runtime |
No | Service command, optional port, init script, and environment. |
volumes |
No | Persistent mounts for runtime containers. |
exports |
No | Static files copied from the built image. |
timers |
No | Cron scripts run inside the running app container. |
Build layers
Section titled “Build layers”Each build layer can copy files and run a script.
type: app
builds: - description: Install Python packages files: ["requirements.txt"] script: | pip install --no-cache-dir -r requirements.txt
- description: Copy app source files: ["*"] script: | python manage.py collectstatic --noinputfiles lists paths from the deploy archive. Use "*" to copy all files.
Files are copied into /app, the container’s working directory. Build scripts run there too.
Use env when a build step needs values from secrets or other units:
type: app
builds: - files: ["*"] env: NPM_TOKEN: ${secret:npm-token} DB_URL: ${app-db:url} script: npm run buildRuntime
Section titled “Runtime”runtime starts the service after deploy.
type: app
runtime: port: 3000 env: NODE_ENV: production init: | npm run migrate cmd: node server.jsport is used for app health checks and app references in domain config:
${myapp:url}becomeshttp://dpl--myapp:<port>${myapp:socket}becomesdpl--myapp:<port>
Use init for short setup work such as database migrations. Use cmd for the long-running process.
Both run with /app as the working directory.
Runtime without a port
Section titled “Runtime without a port”port is optional. Omit it for a service that listens on nothing, such as a queue consumer, a poller, or a container that only hosts timers.
type: appimage: python:3.13-slim
builds: - files: ["*"] script: | pip install --no-cache-dir -r requirements.txt
runtime: env: DB_URL: ${app-db:url} cmd: python worker.pyThis is still a normal supervised container: init, env, volumes, and timers work as usual, and dpl serve restarts it when it exits.
Two things change without a port:
- the deploy health check waits for the container to start and stay running instead of probing a port
${myapp:url}and${myapp:socket}are not valid, because nothing can connect to the app
See Hermes agent worker for a full worker deploy.
Volumes
Section titled “Volumes”Use volumes for data that must survive redeploys.
type: app
volumes: - description: Uploaded files source: myapp-uploads path: /app/uploadssource can be a Podman volume name or a full host path. path must be an absolute container path and cannot be /.
Exports
Section titled “Exports”Use exports for static files that nginx should serve.
type: app
exports: - source: /app/staticfiles path: /staticsource must be an absolute path inside the image and cannot be /. Since the app is installed into /app, the source is usually a path under it:
type: app
exports: - source: /app/.output/public/_nuxt # Nuxt path: /_nuxt - source: /app/staticfiles # Django collectstatic path: /staticA domain unit can serve the files with ${myapp:export}.
Timers
Section titled “Timers”Timers run scripts inside the app container.
type: app
timers: - name: add-time schedule: "* * * * *" script: | python manage.py add_time --seconds 60The schedule uses standard 5-field cron syntax:
- Minute:
10 * * * *- runs at minute 10 of every hour. - Minute (step):
*/5 * * * *- runs every 5 minutes. - Hour:
0 14 * * *- runs at 14:00 every day. - Day of the month:
0 0 15 * *- runs at midnight on the 15th of every month. - Month:
0 0 1 1 *- runs at midnight on January 1st. - Day of the week:
0 0 * * 5- runs at midnight every Friday. - Multiple days:
0 8 * * 1-5- runs at 08:00, Monday through Friday.
Set disabled: true to keep a timer in config but stop dpl from running it.
Timer output is written to:
<base>/state/<unit>/log/timers.log