Revert "Deployment Redesign"

This reverts commit 764d087eac.
This commit is contained in:
Stefan Lazic committed 2026-09-05 09:32:34 +01:00
1 parent e5668b3f23
commit 89bce03ebb
13 files changed
+380 -289

No files matched your search

+31 -83
View File
@@ -17,18 +17,14 @@ The application is intentionally configured with discrete database settings: `DA
## Deployment Model
The intended deployment model is one repeatable Docker deployment for both a
local/self-hosted Debian server and production:
The intended deployment model is a shared multi-business VPS:
- One Dockge-managed Caddy stack owns ports 80 and 443 and routes each domain.
- One Dockge-managed MySQL 8 stack hosts a separate database and user per business.
- Each business is a separate Dockge stack with its own media volume and env file.
- Business containers publish no host ports and share only the proxy and database networks.
- Each business is installed independently through `/install` after its stack starts.
- One Traefik reverse proxy handles HTTPS and routes each business domain.
- One shared MySQL 8 server hosts a separate database and database user for each business.
- Each business runs its own application container and has its own media volume.
- Each business is installed independently through `/install`.
`deploy/business-compose.yaml.template` and `scripts/create-business.sh` are the
canonical business deployment path. The local Docker file remains useful for a
single isolated development instance.
`.env.business.example` is the primary application configuration template. The local Docker files are development tooling and should not be treated as the production deployment model.
## Requirements
@@ -42,8 +38,7 @@ For the primary VPS deployment:
- A Linux VPS with Docker Engine and Docker Compose
- A domain per business, with DNS pointing at the VPS
- Ports 80 and 443 available for Caddy and Let's Encrypt
- Dockge configured to scan the directory containing the generated stacks
- Ports 80 and 443 available for Traefik and Let's Encrypt
For local development:
@@ -155,7 +150,7 @@ Use this path when you already have MySQL running outside Docker or want fast Ne
The seed is idempotent and creates demo business, staff, services, customers, appointments, and related records. It marks the database as installed so the application can be used immediately. Do not run it alongside a real first-run installation.
The direct Node path and the local Docker path should use different `.env.local` values: Docker connects to `next-chair-mysql`, while a host-run Next.js process normally connects to `localhost`. Business stacks use `DATABASE_HOST=nextchair-mysql` on the shared database network.
The direct Node path and the local Docker path should use different `.env.local` values: Docker connects to `next-chair-mysql`, while a host-run Next.js process normally connects to `localhost`. For the business-specific VPS environment, use `.env.business.<slug>` and the matching per-business compose file instead.
## Environment Configuration
@@ -163,9 +158,7 @@ The available templates are:
- `.env.example`: host-run development configuration
- `.env.local.example`: Docker Desktop configuration
- `.env.infra.example`: shared MySQL root settings
- `stacks/caddy/.env.example`: shared Caddy ACME email
- `.env.business.example`: generated business environment reference
- `.env.business.example`: one business in the shared VPS deployment
Required for a basic running installation:
@@ -252,75 +245,35 @@ npm run test:e2e
Playwright uses one Chromium project, one worker, a 45-second test timeout, screenshots on failure, and retains traces on failure. The database should be reset or recreated between isolated test runs when fixtures from previous runs could affect results.
## Docker Server Deployment
## VPS Deployment Reference
This is the same topology for a Debian self-hosted server and a production
server. The only intentional deployment difference is the image tag: build
`nextchair:latest` locally, or use a registry tag such as
`registry.example.com/nextchair:2026-09-04` in production.
The repository also contains a shared multi-business deployment model:
### 1. Prepare the server
- `docker-compose.infra.yaml` runs one Traefik reverse proxy and one shared MySQL 8 server.
- Each business gets its own database and MySQL user, not a shared schema.
- `docker-compose.blake-heritage.yaml` and `docker-compose.marios.yaml` are per-business application examples.
- `.env.business.example` is the template for each business environment file.
Copy this repository's deployment files to a stable directory, for example
`/opt/nextchair`, and configure Dockge to use `/opt/nextchair/stacks` as its
stack directory. The tracked `infra` and `caddy` directories are shared
stacks; every generated business directory sits beside them.
The high-level setup is:
Build or pull the application image once:
1. Create the external network and configure `.env.infra`.
2. Start the shared infrastructure:
```bash
# Self-hosted/local image
docker build -t nextchair:latest .
```bash
docker network create next-chair-edge
docker compose -f docker-compose.infra.yaml --env-file .env.infra up -d
```
# Production image example
docker pull registry.example.com/nextchair:2026-09-04
```
3. Create a separate database and user for the business on the shared MySQL server.
4. Copy `.env.business.example` to `.env.business.<slug>` and set the domain, database credentials, secrets, and `NEXT_PUBLIC_APP_URL`.
5. Start the business compose file:
Create the shared networks and start the two infrastructure stacks:
```bash
docker compose -f docker-compose.blake-heritage.yaml \
--env-file .env.business.blake-heritage up -d --build
```
```bash
cp .env.infra.example .env.infra
cp stacks/caddy/.env.example stacks/caddy/.env
# Edit both env files before starting them.
docker network create nextchair-db
docker network create nextchair-proxy
docker compose -f stacks/infra/compose.yaml up -d
docker compose -f stacks/caddy/compose.yaml up -d
```
The MySQL port is loopback-only by default. Use an SSH tunnel for database
administration rather than opening port 3306 publicly.
### 2. Provision a business
Run this after MySQL is healthy. The script creates the database and least
scope database user, generates secrets, writes a Dockge-ready stack directory,
and writes one Caddy site file:
```bash
NEXTCHAIR_STACK_ROOT=/opt/nextchair/stacks \
NEXTCHAIR_CADDY_SITES_DIR=/opt/nextchair/stacks/caddy/sites \
./scripts/create-business.sh blake-heritage blakeheritage.com nextchair:latest
```
For production, pass the exact image tag to use. The script never overwrites
an existing business slug. In Dockge, the new directory appears as its own
stack, so `start`, `stop`, `restart`, `recreate`, `update`, and logs are scoped
to that business. The generated `.env` is mode `600` and is ignored by git.
After DNS points at the server, start the generated stack from Dockge. Caddy
will obtain the certificate and route the domain to the app container over the
private `nextchair-proxy` network. Reload Caddy after adding or removing a
route:
```bash
docker compose -f stacks/caddy/compose.yaml exec nextchair-caddy caddy reload --config /etc/caddy/Caddyfile
```
Repeat the provisioning command for each business. A business's database,
media volume, env file, app container, and Caddy route are all named from its
slug. The application migration entrypoint runs on every start, while the
first-run business setup remains the app-level `/install` flow.
Traefik terminates HTTPS with Let's Encrypt and routes by the configured `DOMAIN`. Read the comments in [docker-compose.infra.yaml](./docker-compose.infra.yaml) for the exact database provisioning sequence and operational assumptions.
## Repository Layout
@@ -333,9 +286,6 @@ prisma/ Schema, migrations, and development seed
e2e/ Playwright browser journeys and test setup
docker/ Container entrypoint
docs/ Phase implementation notes and historical decisions
deploy/ Reusable business stack template
stacks/ Dockge stacks: infra, caddy, and businesses
scripts/ Database/business provisioning utilities
templates/ UI reference templates
```
@@ -345,9 +295,7 @@ The phase documents in `docs/` contain implementation history and deeper design
**The app redirects to `/install`.** This is expected for a fresh or unseeded database. Set `INSTALLATION_TOKEN` and complete the wizard, or explicitly run the development seed.
**The app cannot connect to MySQL in the local stack.** Use `DATABASE_HOST=next-chair-mysql`, not `localhost`, and ensure the local credentials match the local MySQL container.
**The app cannot connect to MySQL in a business stack.** Use `DATABASE_HOST=nextchair-mysql`, confirm `nextchair-mysql` is attached to `nextchair-db`, and confirm the generated `DATABASE_NAME`, `DATABASE_USER`, and `DATABASE_PASSWORD` match the database created by `scripts/create-business.sh`.
**The app cannot connect to MySQL in Docker.** Use `DATABASE_HOST=next-chair-mysql`, not `localhost`, and ensure the `DATABASE_*` credentials match `MYSQL_DATABASE`, `MYSQL_USER`, and `MYSQL_PASSWORD`.
**The app cannot connect to MySQL when run with `npm run dev`.** Use `DATABASE_HOST=localhost` or the hostname of your external MySQL server, and verify that the database and user already exist.