A single command that runs everything you need for local development.
The plain dev command starts everything you need for local development with a single command:
plain devThis will:
- Run preflight checks
- Execute pending migrations
- Start your development server with auto-reload
- Build and watch CSS with Tailwind (if installed)
- Start required services (like databases)
- Run any custom processes you've defined
The plain dev command does several things:
- Sets
PLAIN_CSRF_TRUSTED_ORIGINSto localhost by default - Runs
plain preflightto check for any issues - Executes any pending model migrations
- Starts
gunicornwith--reload - Serves HTTPS on port 8443 by default (uses the next free port if 8443 is taken and no port is specified)
- Runs
plain tailwind build --watch, ifplain.tailwindis installed - Any custom process defined in
pyproject.tomlattool.plain.dev.run - Necessary services (ex. Postgres) defined in
pyproject.tomlattool.plain.dev.services
Use services to define databases or other processes that your app needs to be functional. The services will be started automatically in plain dev, but also in plain pre-commit (so preflight and tests have a database).
Ultimately, how you run your development database is up to you. But a recommended starting point is to use Docker:
# pyproject.toml
[tool.plain.dev.services]
postgres = {cmd = "docker run --name app-postgres --rm -p 54321:5432 -v $(pwd)/.plain/dev/pgdata:/var/lib/postgresql/data -e POSTGRES_PASSWORD=postgres postgres:15 postgres"}Unlike services, custom processes are only run during plain dev. This is a good place to run something like ngrok or a Plain job worker, which you might need to use your local site, but don't need running for executing tests, for example.
# pyproject.toml
[tool.plain.dev.run]
ngrok = {command = "ngrok http $PORT"}Starts your services by themselves.
Logs are stored in .plain/dev/logs/services/.
Show output from recent plain dev runs.
Logs are stored in .plain/dev/logs/run/.
plain dev logs # print last log
plain dev logs -f # follow the latest log
plain dev logs --pid 1234
plain dev logs --pathManage local database backups. Requires plain.postgres.
A backup is automatically created when plain dev detects pending migrations, so you can easily restore your database if a migration changes something unexpectedly.
plain dev backups list # List all backups
plain dev backups list --branch # Filter to current branch
plain dev backups create # Create a manual backup
plain dev backups create my-backup # Create with a specific name
plain dev backups restore --latest # Restore the most recent backup
plain dev backups restore my-backup # Restore a specific backup
plain dev backups delete my-backup # Delete a specific backup
plain dev backups clear # Delete all backupsBackups are stored in .plain/backups/ and automatically pruned to the 20 most recent per branch.
A built-in pre-commit hook that you can install with plain pre-commit --install.
Runs:
uv lock --check, if using uvplain check(custom commands, code linting, preflight, migrations, tests)plain build
Custom commands can be defined in pyproject.toml at tool.plain.check.run and will run as part of plain check:
[tool.plain.check.run]
my-check = {cmd = "echo 'running my check'"}| Setting | Default | Env var |
|---|---|---|
DEV_REQUESTS_IGNORE_PATHS |
["/favicon.ico"] |
- |
DEV_REQUESTS_MAX |
50 |
- |
See default_settings.py for more details.
You can stop the development server by pressing Ctrl+C in the terminal, or by running plain dev --stop if it was started in the background.
Yes, use the --port or -p option: plain dev --port 8000. If you don't specify a port, it will use 8443 or the next available port.
Use plain dev --start to run the server in the background. You can then use plain dev --stop to stop it.
Services are processes that your app needs to function (like a database). They run during plain dev and also during plain pre-commit. Custom processes only run during plain dev and are typically for development conveniences like ngrok or a job worker.
The development server is configured to show DeprecationWarning and PendingDeprecationWarning messages so you can catch deprecated code before it breaks in future versions. You can override this by setting your own PYTHONWARNINGS environment variable.
Install the plain.dev package from PyPI:
uv add plain.dev --devNote: The plain.dev package does not need to be added to INSTALLED_PACKAGES.
