Automatically start and stop your Databricks Apps on a schedule.
Databricks Apps bill for compute while they're running. If you have apps that only need to be up during business hours, App Automator turns them on in the morning and off at night — automatically — so you don't pay for idle compute overnight and on weekends.
It's a single, tiny Databricks Asset Bundle (DAB) that deploys one serverless job. You list your apps and their hours in a simple YAML file, deploy once, and forget about it.
┌────────────────────┐ every 15 min ┌──────────────────────┐
│ config/apps.yml │ ───────────────────▶ │ Serverless job runs │
│ (your schedule) │ │ src/reconcile.py │
└────────────────────┘ └───────────┬──────────┘
│ compares "should be on?"
│ vs actual app state
▼
starts / stops your apps via the SDK
- Serverless job — no cluster to leave running. Compute spins up only for the few seconds the check takes.
- The job does almost nothing most of the time. It wakes up, reads your config, and in the common case makes zero API calls that change anything.
- Default cadence is every 15 minutes (96 tiny runs/day). Each run is a few seconds of serverless compute. You can make it cheaper still by running less often (see Tuning cost).
- The savings from stopping idle apps dwarf the cost of running this.
- Databricks CLI v0.230+ installed and configured:
databricks --version databricks auth login --host https://<your-workspace>.cloud.databricks.com -p my-workspace
- Serverless compute enabled in your workspace (it is by default in most workspaces).
- Permission to start/stop the apps you want to manage (you need
CAN_MANAGEon them). By default the job runs as you (the deployer).
Open config/apps.yml and list your apps. Use the app
name from databricks apps list (the Name column, not the URL):
settings:
default_timezone: "America/New_York"
apps:
- name: my-dashboard-app # exact name from `databricks apps list`
start: "08:00" # turn ON at 8:00 AM
stop: "19:00" # turn OFF at 7:00 PM
days: [mon, tue, wed, thu, fri] # weekdays only (this is the default)Find your app names with:
databricks apps list -p my-workspacedatabricks bundle deploy -t dev -p my-workspaceThat's it — the job is now live and will reconcile your apps every 15 minutes.
Don't want to wait for the next scheduled run? Trigger it manually:
databricks bundle run app_automator -t dev -p my-workspaceEverything lives in config/apps.yml. Each app supports:
| Field | Required | Description |
|---|---|---|
name |
✅ | App name exactly as in databricks apps list. |
start |
✅ | Time to turn the app on, "HH:MM" 24-hour. |
stop |
✅ | Time to turn the app off, "HH:MM" 24-hour. |
timezone |
– | IANA timezone (e.g. America/Los_Angeles). Defaults to default_timezone. |
days |
– | Days to run: any of [mon,tue,wed,thu,fri,sat,sun]. Defaults to Mon–Fri. |
enabled |
– | false to make the automator ignore this app. Defaults to true. |
Overnight windows work naturally — if stop is earlier than start, the
app stays on through midnight:
- name: my-overnight-app
start: "22:00" # 10 PM
stop: "06:00" # 6 AM next dayApps you don't list are never touched. App Automator only ever starts or stops apps that appear in this file.
After editing the config, redeploy to push the change:
databricks bundle deploy -t dev -p my-workspaceThe job runs src/reconcile.py, which is idempotent:
- Reads
config/apps.yml. - For each app, computes whether it should be running right now (based on
its
start/stop/days/timezone). - Fetches the app's actual compute state via the Databricks SDK.
- If they differ, it calls
apps.start()orapps.stop(). If they already match, it does nothing.
Because it just re-checks state each run, a missed run or a manual start/stop is self-correcting — the next reconcile brings everything back to your schedule.
The check cadence is a bundle variable. Run less often to spend less:
# Every 30 minutes instead of 15:
databricks bundle deploy -t dev -p my-workspace \
--var="reconcile_cron=0 0/30 * * * ?"
# Once an hour, on the hour:
databricks bundle deploy -t dev -p my-workspace \
--var="reconcile_cron=0 0 * * * ?"The trade-off: a coarser cadence means an app might turn on/off up to that many minutes late. 15 minutes is a good default.
Dry run — see what the job would do without starting/stopping anything:
databricks bundle deploy -t dev -p my-workspace --var="dry_run=true"
databricks bundle run app_automator -t dev -p my-workspaceCheck the run output in the Jobs UI (or the CLI output) — every app will be
logged as already ON/OFF or would START/STOP. Set dry_run=false (the
default) and redeploy when you're happy.
Run the logic locally (no deploy needed):
pip install -r requirements-dev.txt
python src/reconcile.py --config config/apps.yml --dry-run true -p my-workspaceUnit tests for the schedule math (no Databricks connection needed):
python tests/test_schedule.pyTear down the job completely:
databricks bundle destroy -t dev -p my-workspaceYour apps are left in whatever state they were in — nothing else is affected.
app-automator/
├── databricks.yml # Bundle definition (name, targets, variables)
├── resources/
│ └── reconcile_job.yml # The serverless job + schedule
├── src/
│ └── reconcile.py # The reconcile logic (start/stop apps)
├── config/
│ └── apps.yml # 👈 YOU EDIT THIS: your apps + schedules
├── tests/
│ └── test_schedule.py # Unit tests for the schedule math
├── requirements-dev.txt
├── LICENSE
└── README.md
Does this delete or redeploy my apps? No. It only calls start/stop on the app's compute. Your app code and deployment are untouched.
What if an app in my config doesn't exist? It's logged as a NOT FOUND
warning and skipped; the run still succeeds and other apps still reconcile.
(So the job won't turn red just because of a typo in one app name.)
Who does the job run as? By default, the identity that deployed the bundle.
That identity needs CAN_MANAGE on each app it controls.
Can I manage apps across multiple workspaces? Deploy the bundle once per
workspace, each with its own config/apps.yml.
MIT — see LICENSE.