# Local development

> pier dev runs the current environment on your machine with docker — the same variables, links and ports the platform injects

URL: https://pier.run/docs/local-dev

```bash
pier dev
```

Builds the environment's apps (the same Dockerfile or detected build the platform uses),
starts them with docker compose alongside a Postgres per declared instance and a MinIO for
declared buckets, injects the same environment variables and links the platform would, and
prints a stable `localhost` port per app.

```bash
pier dev up --dry-run
```

```text title="output"
!  running against env vars and secrets from "prod"
  1 secret(s) would be revealed from server
  Plan
    pg-main:      postgres:17.9 → localhost:7100
    minio:        localhost:7101 — buckets: uploads
    app-api:      pier-dev/acme-1ab0-prod-api:latest → localhost:7103 (build /Users/ian/piercomputing/pier/examples/docs-acme/api)
                                              
 VAR                 SOURCE DETAIL            
                                              
 API_KEY             reveal app               
 DATABASE_URL        link   postgres.main.url 
 LOG_LEVEL           config app               
 PORT                auto                     
 PIER_DB_MAIN_URL    auto                     
 PIER_S3_ENDPOINT    auto                     
 PIER_S3_ACCESS_KEY  auto                     
 PIER_S3_SECRET_KEY  auto                     
 PIER_BUCKET_UPLOADS auto                     
                                              
    www:          site — not emulated
  Generated compose (/Users/ian/Library/Application Support/pier/dev/ws_01ky4wcrsne6s8gtm3jdpaj2tw/proj_01m19h7s7tew3t0505y6q9077c/prod/compose.yaml)
name: pier-dev-acme-1ab0-prod
services:
    app-api:
        image: pier-dev/acme-1ab0-prod-api:latest

```

## What matches the platform

| Surface                                  | Platform                                                                      | `pier dev`                                                           |
| ---------------------------------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| `PORT`                                   | the target's port (default 8080)                                              | the same; published on a stable host port from 7100                  |
| variables                                | environment \< app \< target, secrets from the vault, links resolved at start | the same merge, computed locally; links point at the local stand-ins |
| `DATABASE_URL` and the other link fields | the instance's public DSN                                                     | `postgresql://app:…@db-main:5432/app?sslmode=disable`                |
| Postgres                                 | the declared major, pinned patch                                              | the official image of the same major; data in a named docker volume  |
| buckets                                  | S3 endpoint and keys per project                                              | a local MinIO with one bucket per declaration                        |
| volumes                                  | block storage at `mount_path`                                                 | a named docker volume at `mount_path`                                |
| readiness                                | TCP on `PORT`                                                                 | the same wait before "ready"                                         |

Known differences: `sslmode=disable` locally, no replicas or limits, and compose gives apps
each other's names as DNS (`app-<name>`), which the platform does not.

## Secrets

Values for `secret: true` variables are resolved in order: from `.pier/dev.env` in the
project (local substitutes; gitignored), then from the platform (`--pull-secrets`, which is
always confirmed on a `protected: true` environment). If a required value is still missing
the run fails and lists the keys; `--allow-missing` runs without them. Values are written
to a generated compose file under the user configuration directory and removed by
`pier dev reset`.

## Commands

| Command                    | Does                                                                                           |
| -------------------------- | ---------------------------------------------------------------------------------------------- |
| `pier dev` / `pier dev up` | build what changed, start, wait for ready; `--watch` rebuilds on change; `--only api` a subset |
| `pier dev logs [api]`      | follow the local stack's logs                                                                  |
| `pier dev status`          | this project's local stacks, per environment                                                   |
| `pier dev stop`            | stop; data kept                                                                                |
| `pier dev reset [--all]`   | remove containers, volumes and generated files                                                 |

Docker is the only source of truth for what is running; nothing is cached. Switching
environments with `pier use staging` and running `pier dev` again starts staging's stack
and stops prod's unless `--keep-others`.

## See also

[`pier dev`](/docs/cli/reference/dev) · [Environment variables](/docs/vars) ·
[Use Postgres from your laptop](/docs/guides/use-postgres-locally)
