Skip to content

App

Use an app unit when you need to build a container and run a long-lived service.

type: app
image: 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 cleanup
FieldRequiredPurpose
imageYesBase image used for the build.
buildsYesOrdered build layers.
runtimeNoService command, port, init script, and environment.
volumesNoPersistent mounts for runtime containers.
exportsNoStatic files copied from the built image.
timersNoCron scripts run inside the running app container.

Each build layer can copy files and run a script.

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 --noinput

files lists paths from the deploy archive. Use "*" to copy all files.

Use env when a build step needs values from secrets or other units:

env:
NPM_TOKEN: ${secret:npm-token}
DB_URL: ${app-db:url}

runtime starts the service after deploy.

runtime:
port: 3000
env:
NODE_ENV: production
init: |
npm run migrate
cmd: node server.js

port is used for app health checks and app references in domain config:

  • ${myapp:url} becomes http://dpl--myapp:<port>
  • ${myapp:socket} becomes dpl--myapp:<port>

Use init for short setup work such as database migrations. Use cmd for the long-running process.

Use volumes for data that must survive redeploys.

volumes:
- description: Uploaded files
source: myapp-uploads
path: /app/uploads

source can be a Podman volume name or a full host path. path must be an absolute container path and cannot be /.

Use exports for static files that nginx should serve.

exports:
- source: /app/staticfiles
path: /static

source must be an absolute path inside the image and cannot be /.

A domain unit can serve the files with ${myapp:export}.

Timers run scripts inside the app container.

timers:
- name: add-time
schedule: "* * * * *"
script: |
python manage.py add_time --seconds 60

The 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