# Adaptable local-development example

This is a local Linux-container example to copy/adapt into a real repository root. It is not a production deployment or a complete application. Keep only dependencies the project uses and preserve its existing working scripts/files.

## Files and supported contract

- `compose.yaml`: PostgreSQL 17-style persistent data path, Redis, and an optional `tools` profile database client. The data services use project-scoped named volumes and configurable loopback ports.
- `compose.full.yaml` and `Dockerfile.dev`: an **optional Node/Vite-style development illustration**. The repository must have its real `package.json`, `package-lock.json`, a `dev` script accepting `--host 0.0.0.0` on port 5173, and an appropriate noninteractive `test` script if tests are run. Adapt these to another application; do not convert Go/Python or another frontend framework to Node/Vite to use the standard.
- The full layer's `DB_*`/`CACHE_*` entries illustrate server-side internal addresses. Keep only variables consumed by the actual server application; a browser app never directly uses database credentials. Native Go/Python/server code can use the dependency-only mode and its existing host commands.
- `.env.example`: example image major tags and ports, with no password value. Match the actual supported runtime, deliberately review/pin image updates, and check current upstream image guidance before changing database majors/data mounts. The example tags are not a guarantee of availability, security or production suitability.
- `.dockerignore` and `gitignore.fragment`: merge/adapt exclusions; do not replace existing ignore rules or ignore intentionally versioned fixtures.

## Prepare privately

Copy `.env.example` to untracked `.env.local` using the appropriate native shell/editor. Set a disposable local `POSTGRES_PASSWORD`. Choose a unique `COMPOSE_PROJECT_NAME` and nonconflicting `DB_HOST_PORT`, `CACHE_HOST_PORT`, `APP_HOST_PORT` for each parallel checkout/worktree. The example name is not a global shared environment.

The PostgreSQL image's `POSTGRES_USER` is an initialization/superuser identity, not a least-privilege application identity. For an actual server application, use its real bootstrap/migration process to provision a scoped application role; the full example requires separate `APP_DB_USER`/`APP_DB_PASSWORD`. Do not treat renaming the bootstrap user to `dev` as reducing its privilege. A browser-only app removes these server-only settings instead of receiving database access.

Compose interpolation reads the selected env file, but an existing shell variable can take precedence. Know the effective inputs and avoid ambient production variables. Do not paste a resolved config containing password values into logs or a PR. The Redis example has no authentication and is intended only for the scoped local development environment; production access/authentication needs a separate design.

## Native application plus container dependencies

Run these commands from the adapted repository root when starting that local environment is requested:

```bash
docker compose --env-file .env.local -f compose.yaml config --quiet
docker compose --env-file .env.local -f compose.yaml up -d --wait
docker compose --env-file .env.local -f compose.yaml ps
docker compose --env-file .env.local -f compose.yaml logs --tail=100 db cache
```

Then install/run/debug the application using its verified native commands. A host database client uses `127.0.0.1` and `DB_HOST_PORT`; a container client uses `db:5432`. Cache equivalents are the published host port versus `cache:6379`.

Run the application's actual schema/seed/bootstrap mechanism after dependency readiness. The database image only initializes an empty volume; it does not apply future application migrations. Its healthcheck proves connection readiness, not the application's schema or business behavior.

## Optional application container

```bash
docker compose --env-file .env.local -f compose.yaml -f compose.full.yaml config --quiet
docker compose --env-file .env.local -f compose.yaml -f compose.full.yaml up -d --build --wait
docker compose --env-file .env.local -f compose.yaml -f compose.full.yaml logs --tail=100 app
```

Verify the actual HTTP/user flow separately. An application without a healthcheck is only known to be running by stack wait; this is not application readiness. Source bind mounts permit the developer's source changes to reach the configured reload process; validate permissions/reload on supported hosts.

The image builds dependencies as the `node` user. A separate `app_node_modules` volume keeps Linux dependencies outside host `node_modules`, and can retain old dependencies across image rebuilds. When the lockfile changes, refresh it explicitly in that project's app dependency volume:

```bash
docker compose --env-file .env.local -f compose.yaml -f compose.full.yaml run --rm --no-deps app npm ci
```

Use the same two files for later app lifecycle commands. No `compose.override.yaml` is installed silently, so the app layer is always selected explicitly.

## Optional database client

The `tools` profile defines no additional persistent storage or host port. It uses the project's db credentials and network. The default client command only prints its version; this example query is a read-only connectivity check:

```bash
docker compose --env-file .env.local -f compose.yaml --profile tools run --rm db-client -c "select 1"
```

## Test environment

Create a separate untracked `.env.test` with a **fresh test project name, distinct dependency ports and disposable credentials/data**. Do not reuse `.env.local` or its database. Adapt fixtures/migrations and the repository's actual test command. For the Node illustration:

```bash
docker compose --env-file .env.test -f compose.yaml -f compose.full.yaml run --rm -e CI=true app npm test
```

This command still needs an actual test suite and schema/setup; the template does not provide one. CI should choose a unique scoped project per run, avoid unnecessary host port publication when adapting the model, and collect the test exit status/evidence before cleaning its own disposable environment.

## Stop, teardown and reset

Normal dependency-only stop/teardown retains named data volumes:

```bash
docker compose --env-file .env.local -f compose.yaml stop
docker compose --env-file .env.local -f compose.yaml down
```

For the full mode include `-f compose.full.yaml` in both commands. A requested data reset is a separate destructive operation: resolve the context/project/volume names and data owner first, and decide whether export/backup is needed. Do not add `--volumes` or global prune to normal stop, rebuild or bootstrap. Keep database image/data upgrades separate from disposable-data resets.

## Verification record

Offline config parsing can check interpolation, merged model, ports, dependency conditions, scoped volume/network names and profile declarations without an engine. It cannot prove image availability, build success, application compatibility, runtime access control, hot reload, health, persistence or restoration. State exactly which of those were actually exercised for the adopted project.
