How to Manage Cron Jobs and Deployment Jobs in App Platform

Validated on 11 Feb 2026 • Last edited on 11 Feb 2026

App Platform is a fully managed Platform-as-a-Service (PaaS) that deploys applications from Git repositories or container images. It automatically builds, deploys, and scales components while handling all underlying infrastructure.

Jobs can consist of any application code you want to run at a specific time. App Platform supports two ways to run jobs:

  • Deploy-time jobs, which run before or after a deployment
  • Scheduled (cron) jobs, which run on a recurring schedule using a cron expression

For deploy-time jobs that run after a deployment, you can further choose whether the job runs only after a successful deployment or also after a failed deployment.

Like workers, jobs are not routable, meaning they run in the background and cannot accept HTTP or HTTPS requests.

Note
Scheduled (cron) jobs are billed only for the time they are running. You are not billed while the job is idle between runs.

Add a Job to an App Using Automation

You can add a job to an app using the CLI’s app update command or the API’s app update endpoint. To add a job, update the app’s spec with the job’s specifications and submit the spec using the following command or endpoint.

How to Add a Job to an App Using the DigitalOcean CLI
  1. Install doctl, the official DigitalOcean CLI.
  2. Create a personal access token and save it for use with doctl.
  3. Use the token to grant doctl access to your DigitalOcean account.
    doctl auth init
  4. Finally, run doctl apps update. Basic usage looks like this, but you can read the usage docs for more details:
    doctl apps update <app id> [flags]
    The following example updates an app with the ID f81d4fae-7dec-11d0-a765-00a0c91e6bf6 using an app spec located in a directory called /src/your-app.yaml. Additionally, the command returns the updated app’s ID, ingress information, and creation date:
    doctl apps update f81d4fae-7dec-11d0-a765-00a0c91e6bf6 --spec src/your-app.yaml --format ID,DefaultIngress,Created
How to Add a Job to an App Using the DigitalOcean API
  1. Create a personal access token and save it for use with the API.
  2. Send a PUT request to https://api.digitalocean.com/v2/apps/{id}.

cURL

Using cURL:

curl -X PUT \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
"https://api.digitalocean.com/v2/apps/{id}" \
-d '{"alerts":[{"rule":"DEPLOYMENT_FAILED"},{"rule":"DOMAIN_FAILED"}],"domains":[{"domain":"example.com","type":"PRIMARY","zone":"example.com"}],"envs":[{"key":"API_KEY","scope":"RUN_AND_BUILD_TIME","type":"SECRET","value":"EV[1:zqiRIeaaYK/NqctZDYzy6t0pTrtRDez8:wqGpZRrsKN5nPhWQrS479cfBiXT0WQ==]"}],"features":["buildpack-stack=ubuntu-22"],"ingress":{},"name":"example-app","region":"nyc","services":[{"autoscaling":{"max_instance_count":4,"metrics":{"cpu":{"percent":70}},"min_instance_count":2},"git":{"branch":"main","repo_clone_url":"https://github.com/digitalocean/sample-nodejs.git"},"internal_ports":[8080],"log_destinations":[{"name":"your_log_consumer_name","open_search":{"endpoint":"logs.example.com:12345","basic_auth":{"user":"doadmin","password":"1234567890abcdef"},"index_name":"example-index","cluster_name":"example-cluster"}}],"name":"sample-nodejs","run_command":"yarn start","source_dir":"/"}]}'

Python

Using PyDo, the official DigitalOcean API client for Python:

import os
from pydo import Client

client = Client(token=os.environ.get("DIGITALOCEAN_TOKEN"))
req = {
"spec": {
    "name": "web-app-01",
    "region": "nyc",
    "domains": [
        {
            "domain": "app.example.com",
            "type": "DEFAULT",
            "wildcard": True,
            "zone": "example.com",
            "minimum_tls_version": "1.3",
        }
    ],
    "services": [],
    "static_sites": [
        {
            "cors": {
                "allow_origins": [
                    {"exact": "https://www.example.com"},
                    {"regex": "^.*example.com"},
                ],
                "allow_methods": [
                    "GET",
                    "OPTIONS",
                    "POST",
                    "PUT",
                    "PATCH",
                    "DELETE",
                ],
                "allow_headers": ["Content-Type", "X-Custom-Header"],
                "expose_headers": ["Content-Encoding", "X-Custom-Header"],
                "max_age": "5h30m",
                "allow_credentials": False,
            },
            "routes": [{"path": "/api", "preserve_path_prefix": True}],
        }
    ],
    "jobs": [
        {
            "name": "api",
            "gitlab": {
                "branch": "main",
                "deploy_on_push": True,
                "repo": "digitalocean/sample-golang",
            },
            "image": {
                "registry": "registry.hub.docker.com",
                "registry_type": "DOCR",
                "repository": "origin/master",
                "tag": "latest",
            },
            "dockerfile_path": "path/to/Dockerfile",
            "build_command": "npm run build",
            "run_command": "bin/api",
            "source_dir": "path/to/dir",
            "envs": [
                {
                    "key": "BASE_URL",
                    "scope": "BUILD_TIME",
                    "type": "GENERAL",
                    "value": "http://example.com",
                }
            ],
            "environment_slug": "node-js",
            "log_destinations": {
                "name": "my_log_destination",
                "papertrail": {
                    "endpoint": "https://mypapertrailendpoint.com"
                },
                "datadog": {
                    "endpoint": "https://mydatadogendpoint.com",
                    "api_key": "abcdefghijklmnopqrstuvwxyz0123456789",
                },
                "logtail": {
                    "token": "abcdefghijklmnopqrstuvwxyz0123456789"
                },
               "open_search": {
                    "endpoint": "https://myopensearchendpoint.com:9300"
                    "index_name": "logs"
                    "basic_auth": {
                        "user": "doadmin",
                        "password": "password"
                    }
                },
            },
            "instance_count": 2,
            "instance_size_slug": "apps-s-1vcpu-0.5gb",
            "kind": "PRE_DEPLOY",
        }
    ],
    "workers": [
        {
            "name": "api",
            "gitlab": {
                "branch": "main",
                "deploy_on_push": True,
                "repo": "digitalocean/sample-golang",
            },
            "image": {
                "registry": "registry.hub.docker.com",
                "registry_type": "DOCR",
                "repository": "origin/master",
                "tag": "latest",
            },
            "dockerfile_path": "path/to/Dockerfile",
            "build_command": "npm run build",
            "run_command": "bin/api",
            "source_dir": "path/to/dir",
            "envs": [
                {
                    "key": "BASE_URL",
                    "scope": "BUILD_TIME",
                    "type": "GENERAL",
                    "value": "http://example.com",
                }
            ],
            "environment_slug": "node-js",
            "log_destinations": {
                "name": "my_log_destination",
                "papertrail": {
                    "endpoint": "https://mypapertrailendpoint.com"
                },
                "datadog": {
                    "endpoint": "https://mydatadogendpoint.com",
                    "api_key": "abcdefghijklmnopqrstuvwxyz0123456789",
                },
                "logtail": {
                    "token": "abcdefghijklmnopqrstuvwxyz0123456789"
                },
               "open_search": {
                    "endpoint": "https://myopensearchendpoint.com:9300"
                    "index_name": "logs"
                    "basic_auth": {
                        "user": "doadmin",
                        "password": "password"
                    }
                },
            },
            "instance_count": 2,
            "instance_size_slug": "apps-s-1vcpu-0.5gb",
        }
    ],
    "functions": [
        {
            "cors": {
                "allow_origins": [
                    {"exact": "https://www.example.com"},
                    {"regex": "^.*example.com"},
                ],
                "allow_methods": [
                    "GET",
                    "OPTIONS",
                    "POST",
                    "PUT",
                    "PATCH",
                    "DELETE",
                ],
                "allow_headers": ["Content-Type", "X-Custom-Header"],
                "expose_headers": ["Content-Encoding", "X-Custom-Header"],
                "max_age": "5h30m",
                "allow_credentials": False,
            },
            "routes": [{"path": "/api", "preserve_path_prefix": True}],
            "name": "api",
            "source_dir": "path/to/dir",
            "alerts": [
                {
                    "rule": "CPU_UTILIZATION",
                    "disabled": False,
                    "operator": "GREATER_THAN",
                    "value": 2.32,
                    "window": "FIVE_MINUTES",
                }
            ],
            "envs": [
                {
                    "key": "BASE_URL",
                    "scope": "BUILD_TIME",
                    "type": "GENERAL",
                    "value": "http://example.com",
                }
            ],
            "gitlab": {
                "branch": "main",
                "deploy_on_push": True,
                "repo": "digitalocean/sample-golang",
            },
            "log_destinations": {
                "name": "my_log_destination",
                "papertrail": {
                    "endpoint": "https://mypapertrailendpoint.com"
                },
                "datadog": {
                    "endpoint": "https://mydatadogendpoint.com",
                    "api_key": "abcdefghijklmnopqrstuvwxyz0123456789",
                },
                "logtail": {
                    "token": "abcdefghijklmnopqrstuvwxyz0123456789"
                },
               "open_search": {
                    "endpoint": "https://myopensearchendpoint.com:9300"
                    "index_name": "logs"
                    "basic_auth": {
                        "user": "doadmin",
                        "password": "password"
                    }
                },
            },
        }
    ],
    "databases": [
        {
            "cluster_name": "cluster_name",
            "db_name": "my_db",
            "db_user": "superuser",
            "engine": "PG",
            "name": "prod-db",
            "production": True,
            "version": "12",
        }
    ],
    vpc: {
        id: c22d8f48-4bc4-49f5-8ca0-58e7164427ac,
    }
}
update_resp = client.apps.update(id="bb245ba", body=req)

Add a Job to an App Using the Control Panel

The following steps show how to create a scheduled (cron) job. Other job types follow the same process, with slight differences that are noted.

  1. Go to the Apps page and select your app. Click Add components, then choose Create resources from source code.
An example app is shown with the Add components button selected and the Create resources from source code option is highlighted.
  1. On the Add resources screen:

    • Select your source, either a Git repository or a container image.
    • If using a Git repository, choose a branch to deploy from. By default, App Platform builds from the repository’s root. If your job code lives in a subdirectory (for example, in a monorepo), set the source directory accordingly.
    • Clear the Autodeploy checkbox if you want to stop automatic redeploys when the repository changes.
    • Click Next.
The Add resources screen showing a GitHub repository selected. The Autodeploy option is also selected.

App Platform inspects the code and app resources and selects an appropriate runtime environment (such as Node or Ruby). The screen updates and displays the Resource settings table.

  1. In the Resource settings table, in the Info section, click Edit. In the Resource type dropdown menu, select Job.
Info section showing the expanded Resource type dropdown menu with Job selected.
  1. In the Deployment settings section, verify the run command for the job. If needed, click Edit to update it. If no run command is specified, App Platform uses the default command for the runtime (for example, npm start for Node.js components). For Dockerfile-based builds, entering a run command overrides the Dockerfile’s entrypoint.
The Deployment settings section, showing the Node.js run command npm start.
  1. In the Job trigger section:

    • Click Edit, then select On a schedule for a cron job. For other jobs, the options include After every failed deploy, After every successful deploy, and Before every deploy.
    • (Cron only) In the Schedule field, enter a cron expression to run the job at a specific time or interval (minimum every 15 minutes).
The Job Trigger section showing On a schedule selected, and a cron expression that runs the job every 30 minutes.
  1. If needed, update environment variables or the instance size for the job. Environment variables are often required for database credentials or API keys. For resource-intensive jobs, you can increase the instance size.
Note
Scheduled (cron) jobs are billed only for the time they are running. You are not billed while the job is idle between runs.
  1. When finished, click Add resources to add the job and redeploy your app.

After the job deploys, it appears as a component at the top of the Settings tab.

The Settings tab showing the app and its components, including a job component.

To view the job’s activity, go to the Activity tab and click Jobs.

The Activity tab showing each time the cron job has run on a 15-minute interval.

Schedule Jobs Using the App Spec

To schedule a job, update the app spec with the following template, setting kind to SCHEDULED and configuring schedule with a cron expression (minimum of every 15 minutes) and time zone like this:

jobs:
- name: hello-world
  kind: SCHEDULED
  schedule:
    cron: "*/15 * * * *"
    time_zone: Asia/Kolkata
  ...
How to View Past Invocations Using the DigitalOcean API
  1. Create a personal access token and save it for use with the API.
  2. Send a GET request to https://api.digitalocean.com/v2/apps/<app-id>/jobs/<job-name>/invocations.
How to View Job Logs Using the DigitalOcean API
  1. Create a personal access token and save it for use with the API.
  2. Send a GET request to https://api.digitalocean.com/v2/apps/<app-id>/jobs/<job-name>/invocations.

Edit Jobs

Go to the Apps page, select your app, then click the Settings tab. Select the job you want to edit.

Edit the settings as needed to change the job's scaling behavior, environment variables, commands, and more.

You can also edit the Job Trigger to change when the job runs.

Job trigger drop-down

Destroy Jobs

Go to the Apps page, select your app, then click the Settings tab. Select the job you want to destroy.

At the bottom of the page, click Destroy Component. Enter the job's name to confirm, then click Destroy.

We can't find any results for your search.

Try using different keywords or simplifying your search terms.