Skip to content

The Bench CLI

Bench is the command-line tool that orchestrates your entire Frappe ecosystem. It manages sites, apps, database operations, background workers, deployments, and more. Think of it as docker compose + npm + django-admin rolled into one — purpose-built for Frappe.

Every Frappe operation goes through bench. You never interact with Gunicorn, Redis, or MariaDB directly in day-to-day development.

Terminal window
# See all available commands
bench --help
# Output (truncated):
# Usage: bench [OPTIONS] COMMAND [ARGS]...
#
# Commands:
# backup Backup site
# build Build JS and CSS assets
# clear-cache Clear cache, doctype cache, and defaults
# console Start interactive Python console for a site
# disable-scheduler Disable scheduler
# doctor Diagnose scheduler and worker health
# drop-site Remove a site completely
# enable-scheduler Enable scheduler
# execute Execute a function
# export-csv Export DocType data as CSV
# get-app Clone and install a Frappe app
# init Initialize a new bench instance
# install-app Install app on a site
# migrate Run database migrations and patches
# new-app Create a new Frappe app scaffold
# new-site Create a new site
# pip Run pip in bench virtualenv
# remove-app Remove app from bench
# restart Restart production services
# restore Restore site from backup
# run-tests Run test suite
# set-config Set site or common config values
# setup Setup utilities (production, nginx, etc.)
# start Start development server
# switch-to-branch Switch app to a different branch
# update Pull latest changes and update bench
# use Set default site
# version Show versions of all installed apps
# watch Watch and rebuild assets on changes
# ...

A site is a tenant with its own database and files. These commands create, switch between, configure, and remove sites on the bench.

Terminal window
# Basic site creation
bench new-site icecream.localhost --mariadb-root-password mypassword
# With specific admin password
bench new-site icecream.localhost \
--mariadb-root-password mypassword \
--admin-password SecurePass123
# With a specific database name
bench new-site icecream.localhost \
--mariadb-root-password mypassword \
--db-name icecream_db
# Using Postgres instead of MariaDB
bench new-site icecream.localhost \
--db-type postgres \
--db-host localhost \
--db-port 5432
Terminal window
# Set the default site (used when --site is omitted)
bench use icecream.localhost
# List all sites
ls sites/*/site_config.json
# Output:
# sites/icecream.localhost/site_config.json
# sites/franchise-hq.localhost/site_config.json
# Set a configuration value for a site
bench --site icecream.localhost set-config key value
# Set a configuration value for all sites (common config)
bench set-config -g key value
# View site config
cat sites/icecream.localhost/site_config.json
Terminal window
# Drop a site (removes database, files, and site directory)
bench drop-site test-site.localhost --mariadb-root-password mypassword
# Force drop without confirmation
bench drop-site test-site.localhost --force --mariadb-root-password mypassword

bench migrate is how schema changes reach the database — it reads your DocType JSON, alters tables, runs patches, and rebuilds the search index. There are no hand-written migration files.

Terminal window
# Migrate a specific site (applies schema changes, patches, rebuilds search)
bench --site icecream.localhost migrate
# Migrate all sites on the bench
bench --site all migrate
# Expected output:
# Migrating icecream.localhost
# Updating DocTypes for frappe: [========================================] 100%
# Updating DocTypes for erpnext: [========================================] 100%
# Running patches...
# Updating Dashboard...

Apps are the Python/JavaScript packages installed on a bench. These commands fetch them, scaffold new ones, and install or remove them per site.

Terminal window
# Get an app from GitHub (specific branch)
bench get-app erpnext --branch version-16
# Get an app from a custom repository
bench get-app https://github.com/your-org/ice_cream_shop.git --branch main
# Get an app from a local path
bench get-app /path/to/local/ice_cream_shop
Terminal window
bench new-app ice_cream_shop
# Interactive prompts:
# App Title (default: Ice Cream Shop): Ice Cream Shop
# App Description: Custom app for franchise ice cream business management
# App Publisher: Your Name
# App Email: dev@icecreamshop.com
# App License (default: MIT): MIT
# Expected output:
# App ice_cream_shop created at apps/ice_cream_shop

This creates the following scaffold:

  • Directoryapps/ice_cream_shop/
    • Directoryice_cream_shop/
      • __init__.py
      • hooks.py app configuration and event hooks
      • patches.txt database migration patches
      • modules.txt list of modules in this app
      • Directorytemplates/
        • __init__.py
      • Directoryice_cream_shop/ default module (same name as app)
        • __init__.py
      • Directorypublic/ static assets (JS, CSS, images)
      • Directorywww/ website pages
    • setup.py
    • pyproject.toml
    • requirements.txt
    • license.txt
    • README.md
Terminal window
# Install an app on a specific site
bench --site icecream.localhost install-app ice_cream_shop
# Remove an app from a site
bench --site icecream.localhost remove-app ice_cream_shop
# Completely remove an app from the bench (uninstalls from all sites first)
bench remove-app ice_cream_shop
Terminal window
# Start all development processes (web, socketio, redis, workers, watch)
bench start
# This reads the Procfile and starts:
# web: bench serve --port 8000
# socketio: /usr/bin/node apps/frappe/socketio.js
# watch: bench watch
# schedule: bench schedule
# worker: bench worker --queue short,default,long

bench start is a convenience wrapper that launches all processes defined in the Procfile. Press Ctrl+C to stop all processes.

Terminal window
# Build all JS and CSS assets for all apps
bench build
# Build assets for a specific app only
bench build --app ice_cream_shop
# Expected output:
# Building ice_cream_shop...
# DONE in 2.34s
# Watch for file changes and rebuild automatically
bench watch
# This is already included in `bench start`, but useful
# if you are running the web server separately via VS Code debugger
Terminal window
# Clear all caches (Redis + local)
bench --site icecream.localhost clear-cache
# Clear website-specific cache (template cache, page cache)
bench --site icecream.localhost clear-website-cache
# When to clear cache:
# - After modifying hooks.py
# - After changing DocType definitions manually
# - When the UI shows stale data after code changes
# - After switching branches

The console is your most-used debugging tool. Where in a Node.js project you might drop into node with your app required in, here you get an IPython REPL with the full Frappe context already wired up.

Terminal window
# Open a Python console with Frappe context loaded for a site
bench --site icecream.localhost console
# Inside the console:
# In [1]: frappe.get_doc("User", "Administrator")
# Out[1]: <frappe.core.doctype.user.user.User object at 0x...>
#
# In [2]: frappe.db.sql("SELECT name, email FROM tabUser LIMIT 5")
# Out[2]: (('Administrator', 'admin@example.com'), ('Guest', 'guest@example.com'))
#
# In [3]: frappe.db.count("Sales Invoice")
# Out[3]: 42
#
# In [4]: doc = frappe.get_doc({
# "doctype": "Ice Cream Flavor",
# "flavor_name": "Mango Alphonso",
# "base_type": "Sorbet",
# "cost_per_scoop": 45.00
# })
# In [5]: doc.insert()
# In [6]: frappe.db.commit()

The console is an IPython REPL with the full Frappe context — database connection, session, caching — all wired up. This is your go-to tool for exploring data, testing queries, and debugging.

Terminal window
# Execute any Python function in the Frappe context
bench --site icecream.localhost execute frappe.client.get_count \
--args '["Sales Invoice"]'
# Execute a function from your custom app
bench --site icecream.localhost execute \
ice_cream_shop.tasks.sync_franchise_inventory \
--kwargs '{"franchise_id": "FRAN-001"}'
Terminal window
# Run all tests for an app
bench --site icecream.localhost run-tests --app ice_cream_shop
# Run tests for a specific DocType
bench --site icecream.localhost run-tests \
--doctype "Ice Cream Flavor"
# Run a specific test file
bench --site icecream.localhost run-tests \
--module ice_cream_shop.ice_cream_shop.doctype.ice_cream_flavor.test_ice_cream_flavor
# Run with verbose output
bench --site icecream.localhost run-tests --app ice_cream_shop -v
# Expected output:
# Running tests for ice_cream_shop
# .....
# Ran 5 tests in 3.456s
# OK
Terminal window
# Open a MariaDB shell for the site's database
bench --site icecream.localhost mariadb
# MariaDB [icecream_db]> SHOW TABLES LIKE '%flavor%';
# +-----------------------------------+
# | Tables_in_icecream_db (%flavor%) |
# +-----------------------------------+
# | tabIce Cream Flavor |
# +-----------------------------------+
#
# MariaDB [icecream_db]> SELECT name, base_type, cost_per_scoop
# FROM `tabIce Cream Flavor` LIMIT 5;
Terminal window
# Basic database backup
bench --site icecream.localhost backup
# Expected output:
# Backup for site icecream.localhost:
# Database: ~/frappe-bench/sites/icecream.localhost/private/backups/
# 20260320_143000-icecream_localhost-database.sql.gz
# Full backup with public and private files
bench --site icecream.localhost backup --with-files
# Expected output:
# Database: .../20260320_143000-icecream_localhost-database.sql.gz
# Private: .../20260320_143000-icecream_localhost-private-files.tar
# Public: .../20260320_143000-icecream_localhost-files.tar
# Backup to a specific path with compression
bench --site icecream.localhost backup \
--with-files \
--compress \
--backup-path /tmp/backups/
# Backup all sites
bench --site all backup --with-files
Terminal window
# Restore database only
bench --site icecream.localhost restore \
/path/to/20260320_143000-icecream_localhost-database.sql.gz
# Restore with files
bench --site icecream.localhost restore \
/path/to/20260320_143000-icecream_localhost-database.sql.gz \
--with-private-files /path/to/20260320_143000-icecream_localhost-private-files.tar \
--with-public-files /path/to/20260320_143000-icecream_localhost-files.tar
# After restoring, always migrate
bench --site icecream.localhost migrate

These commands generate the Nginx and Supervisor configs that turn a dev bench into a managed production deployment.

Terminal window
# Generate Nginx and Supervisor configuration, set up production
sudo bench setup production <your-linux-username>
# This command:
# 1. Generates /etc/nginx/conf.d/frappe-bench.conf
# 2. Generates /etc/supervisor/conf.d/frappe-bench.conf
# 3. Enables and starts Supervisor and Nginx
# 4. Enables the scheduler
# If you need to regenerate configs after changes:
sudo bench setup nginx
sudo bench setup supervisor
# Restart production services
sudo bench restart
Terminal window
# Set up Let's Encrypt SSL for a site
sudo bench setup lets-encrypt icecream.example.com
# For multi-tenant setups with wildcard SSL
sudo bench setup wildcard-ssl example.com

The scheduler runs the periodic jobs defined in your app’s hooks.py. It must be enabled for background jobs to fire in production.

Terminal window
# Enable the scheduler (required for background jobs in production)
bench --site icecream.localhost enable-scheduler
# Disable the scheduler (useful during maintenance)
bench --site icecream.localhost disable-scheduler
# Check the health of scheduler and workers
bench doctor
# Expected output:
# -----Checking scheduler status-----
# icecream.localhost: Active
#
# -----Checking worker status-----
# Workers online: 3
# -----active_jobs-----
# No active jobs
# -----queued_jobs-----
# No queued jobs

Frappe supports hosting multiple sites on a single bench, accessible via different domain names. This is how a single bench can serve every ScoopJoy franchise from one codebase.

Terminal window
# Enable DNS-based multi-tenancy
bench config dns_multitenant on
# Create additional sites
bench new-site franchise-north.example.com --mariadb-root-password mypassword
bench new-site franchise-south.example.com --mariadb-root-password mypassword
# Install apps on each site
bench --site franchise-north.example.com install-app erpnext
bench --site franchise-north.example.com install-app ice_cream_shop
# Add a custom domain to a site
bench setup add-domain franchise-north.example.com --site franchise-north.example.com
# Regenerate Nginx config to include the new sites
bench setup nginx
sudo service nginx reload
Terminal window
# Full update: pulls code, installs requirements, builds, migrates, restarts
bench update
# Update without restarting (useful during development)
bench update --no-restart
# Only pull code without migrating
bench update --pull
# Update a specific app only
bench update --apps erpnext
# Reset to upstream code (discards local changes -- use with caution)
bench update --reset
Terminal window
# Switch an app to a different branch
bench switch-to-branch version-16 frappe erpnext
# Switch only ERPNext to develop branch
bench switch-to-branch develop erpnext
# After switching branches, always migrate
bench --site icecream.localhost migrate

Practical workflow: from zero to running custom app

Section titled “Practical workflow: from zero to running custom app”

Here is a complete, end-to-end workflow that ties all the bench commands together. This is the sequence you will follow for every new project — and it reuses the native install from Development Environment Setup.

Terminal window
# 1. Initialize a new bench
bench init ice-cream-bench --frappe-branch version-16 --python python3.14
cd ice-cream-bench
# 2. Create a site
bench new-site icecream.localhost --mariadb-root-password mypassword --admin-password admin123
bench use icecream.localhost
# 3. Get and install ERPNext
bench get-app erpnext --branch version-16
bench --site icecream.localhost install-app erpnext
# 4. Create your custom app
bench new-app ice_cream_shop
bench --site icecream.localhost install-app ice_cream_shop
# 5. Start developing
bench start
# (Open http://icecream.localhost:8000 in your browser)
# 6. After making code changes, build and test
bench build --app ice_cream_shop
bench --site icecream.localhost run-tests --app ice_cream_shop
# 7. When you modify DocTypes, migrate
bench --site icecream.localhost migrate
# 8. Before calling it a day, back up
bench --site icecream.localhost backup --with-files
# 9. Clear cache if anything looks stale
bench --site icecream.localhost clear-cache
# 10. Check system health
bench doctor
CommandPurpose
bench startStart dev server
bench new-site <name>Create a new site
bench use <name>Set default site
bench get-app <url/name>Download an app
bench --site <name> install-app <app>Install app on site
bench new-app <name>Scaffold a new app
bench --site <name> migrateRun migrations and patches
bench --site <name> clear-cacheClear all caches
bench buildBuild JS/CSS assets
bench --site <name> consolePython REPL with Frappe context
bench --site <name> backup --with-filesFull backup
bench --site <name> restore <file>Restore from backup
bench --site <name> mariadbOpen database shell
bench --site <name> run-tests --app <app>Run tests
bench doctorCheck scheduler and worker health
bench updatePull, build, migrate, restart
bench --site <name> set-config <k> <v>Set site config value
bench execute <dotted.path>Run a Python function