Plain is headed towards 1.0! Subscribe for development updates →

  1# plain.dev
  2
  3**A single command that runs everything you need for local development.**
  4
  5![Plain dev command example](https://github.com/dropseed/plain/assets/649496/3643bb64-a99b-4a8e-adab-8c6b81791ea9)
  6
  7- [Overview](#overview)
  8- [Commands](#commands)
  9    - [`plain dev`](#plain-dev)
 10        - [Services](#services)
 11        - [Custom processes](#custom-processes)
 12    - [`plain dev services`](#plain-dev-services)
 13    - [`plain dev logs`](#plain-dev-logs)
 14    - [`plain pre-commit`](#plain-pre-commit)
 15- [VS Code debugging](#vs-code-debugging)
 16- [Installation](#installation)
 17
 18## Overview
 19
 20The `plain.dev` package provides development tools for Plain applications. The main command, `plain dev`, starts everything you need for local development with a single command:
 21
 22```bash
 23plain dev
 24```
 25
 26This will:
 27
 28- Run preflight checks
 29- Execute pending migrations
 30- Start your development server with auto-reload
 31- Build and watch CSS with Tailwind (if installed)
 32- Start required services (like databases)
 33- Run any custom processes you've defined
 34
 35## Commands
 36
 37### `plain dev`
 38
 39The `plain dev` command does several things:
 40
 41- Sets `PLAIN_CSRF_TRUSTED_ORIGINS` to localhost by default
 42- Runs `plain preflight` to check for any issues
 43- Executes any pending model migrations
 44- Starts `gunicorn` with `--reload`
 45- Serves HTTPS on port 8443 by default (uses the next free port if 8443 is taken and no port is specified)
 46- Runs `plain tailwind build --watch`, if `plain.tailwind` is installed
 47- Any custom process defined in `pyproject.toml` at `tool.plain.dev.run`
 48- Necessary services (ex. Postgres) defined in `pyproject.toml` at `tool.plain.dev.services`
 49
 50#### Services
 51
 52Use 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).
 53
 54Ultimately, how you run your development database is up to you. But a recommended starting point is to use Docker:
 55
 56```toml
 57# pyproject.toml
 58[tool.plain.dev.services]
 59postgres = {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"}
 60```
 61
 62#### Custom processes
 63
 64Unlike [services](#services), custom processes are _only_ run during `plain dev`. This is a good place to run something like [ngrok](https://ngrok.com/) or a [Plain job worker](../../../plain-jobs), which you might need to use your local site, but don't need running for executing tests, for example.
 65
 66```toml
 67# pyproject.toml
 68[tool.plain.dev.run]
 69    ngrok = {command = "ngrok http $PORT"}
 70```
 71
 72### `plain dev services`
 73
 74Starts your [services](#services) by themselves.
 75Logs are stored in `.plain/dev/logs/services/`.
 76
 77### `plain dev logs`
 78
 79Show output from recent `plain dev` runs.
 80
 81Logs are stored in `.plain/dev/logs/run/`.
 82
 83```bash
 84plain dev logs        # print last log
 85plain dev logs -f     # follow the latest log
 86plain dev logs --pid 1234
 87plain dev logs --path
 88```
 89
 90### `plain pre-commit`
 91
 92A built-in pre-commit hook that can be installed with `plain pre-commit --install`.
 93
 94Runs:
 95
 96- Custom commands defined in `pyproject.toml` at `tool.plain.pre-commit.run`
 97- `plain code check`, if [`plain.code`](https://plainframework.com/docs/plain-code/plain/code/) is installed
 98- `uv lock --locked`, if using uv
 99- `plain preflight`
100- `plain migrate --check`
101- `plain makemigrations --dry-run --check`
102- `plain build`
103- `plain test`
104
105## VS Code debugging
106
107![Debug Plain with VS Code](https://github.com/dropseed/plain-public/assets/649496/250138b6-7702-4ab6-bf38-e0c8e3c56d06)
108
109Since `plain dev` runs multiple processes at once, the regular [pdb](https://docs.python.org/3/library/pdb.html) debuggers don't quite work.
110
111Instead, we include [microsoft/debugpy](https://github.com/microsoft/debugpy) and provide debugging utilities to make it easier to use VS Code's debugger.
112
113First, import and run the `debug.attach()` function:
114
115```python
116class HomeView(TemplateView):
117    template_name = "home.html"
118
119    def get_template_context(self):
120        context = super().get_template_context()
121
122        # Make sure the debugger is attached (will need to be if runserver reloads)
123        from plain.dev import debug; debug.attach()
124
125        # Add a breakpoint (or use the gutter in VS Code to add one)
126        breakpoint()
127
128        return context
129```
130
131When you load the page, you'll see "Waiting for debugger to attach...".
132
133You can then run the VS Code debugger and attach to an existing Python process, at localhost:5678.
134
135## Installation
136
137Install the `plain.dev` package from [PyPI](https://pypi.org/project/plain.dev/):
138
139```bash
140uv add plain.dev --dev
141```
142
143Note: The `plain.dev` package does not need to be added to `INSTALLED_PACKAGES`.