Removed dockge from the app as it is not required due to the manager page picking up most of its abilities.
This commit is contained in:
10 files changed
+74
-76
No files matched your search
@@ -22,15 +22,3 @@ ACME_EMAIL="you@example.com"
|
|||||||
# testing on a machine already using those ports for something else.
|
# testing on a machine already using those ports for something else.
|
||||||
CADDY_HTTP_PORT=80
|
CADDY_HTTP_PORT=80
|
||||||
CADDY_HTTPS_PORT=443
|
CADDY_HTTPS_PORT=443
|
||||||
|
|
||||||
# ── Dockge (stack management UI — http://<vps>:5001) ───────────────────
|
|
||||||
# This repo's absolute path on the VPS, e.g. "/opt/nextchair/stacks" — NOT
|
|
||||||
# a relative path. Dockge (and the management UI in manager/) shell
|
|
||||||
# `docker compose` out to the HOST's dockerd via the mounted Docker
|
|
||||||
# socket, so a compose-file path only resolves to the same file on both
|
|
||||||
# sides if the bind-mount doesn't change it — meaning this must be
|
|
||||||
# mounted at the identical path inside the container too (see
|
|
||||||
# stacks/infra/compose.yaml's dockge service). Get this wrong and Dockge
|
|
||||||
# will either fail to find your stacks or manage the wrong path entirely.
|
|
||||||
STACKS_DIR="/opt/nextchair/stacks"
|
|
||||||
DOCKGE_HOST_PORT=5001
|
|
||||||
+6
-5
@@ -27,11 +27,12 @@ dist/
|
|||||||
|
|
||||||
# per-deployment production env files (multi-business VPS setup — see
|
# per-deployment production env files (multi-business VPS setup — see
|
||||||
# stacks/infra/compose.yaml / stacks/<business>/compose.yaml). Every real
|
# stacks/infra/compose.yaml / stacks/<business>/compose.yaml). Every real
|
||||||
# stack's .env lives at stacks/<name>/.env (Dockge's own directory
|
# stack's .env lives at stacks/<name>/.env (one directory per stack,
|
||||||
# convention) — this one wildcard rule covers every current and future
|
# same convention NextChair Manager's provisioning uses) — this one
|
||||||
# stack without another .gitignore edit. .env.infra.example and
|
# wildcard rule covers every current and future stack without another
|
||||||
# .env.business.example (the templates you copy into each stacks/<name>/
|
# .gitignore edit. .env.infra.example and .env.business.example (the
|
||||||
# directory) stay tracked, same as .env.local.example above.
|
# templates you copy into each stacks/<name>/ directory) stay tracked,
|
||||||
|
# same as .env.local.example above.
|
||||||
stacks/*/.env
|
stacks/*/.env
|
||||||
|
|
||||||
# Real per-business Caddy site files (deployment-specific domains, not
|
# Real per-business Caddy site files (deployment-specific domains, not
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ docker run --rm node:22-alpine sh -c \
|
|||||||
node -e \"const h=require('/tmp/gen/node_modules/bcryptjs').hashSync('your-password',12); console.log('MANAGER_PASSWORD_HASH=\\\"'+h.replace(/\\$/g,'\\$\\$')+'\\\"')\""
|
node -e \"const h=require('/tmp/gen/node_modules/bcryptjs').hashSync('your-password',12); console.log('MANAGER_PASSWORD_HASH=\\\"'+h.replace(/\\$/g,'\\$\\$')+'\\\"')\""
|
||||||
```
|
```
|
||||||
|
|
||||||
### 1. Shared infrastructure — network, MySQL, Caddy, Dockge
|
### 1. Shared infrastructure — network, MySQL and Caddy
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker network create next-chair-edge
|
docker network create next-chair-edge
|
||||||
@@ -60,7 +60,6 @@ Edit `stacks/infra/.env`:
|
|||||||
- `MYSQL_ROOT_PASSWORD` — the first secret you generated above.
|
- `MYSQL_ROOT_PASSWORD` — the first secret you generated above.
|
||||||
- `MYSQL_HOST_PORT` — set this to `127.0.0.1:3306` so MySQL is only exposed on the VPS loopback interface, not publicly. You can still connect to it remotely through an SSH tunnel.
|
- `MYSQL_HOST_PORT` — set this to `127.0.0.1:3306` so MySQL is only exposed on the VPS loopback interface, not publicly. You can still connect to it remotely through an SSH tunnel.
|
||||||
- `ACME_EMAIL` — a real address you check (Let's Encrypt renewal notices).
|
- `ACME_EMAIL` — a real address you check (Let's Encrypt renewal notices).
|
||||||
- `STACKS_DIR` — the absolute clone path, e.g. `/opt/nextchair/stacks`.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose -f stacks/infra/compose.yaml --env-file stacks/infra/.env up -d
|
docker compose -f stacks/infra/compose.yaml --env-file stacks/infra/.env up -d
|
||||||
@@ -189,3 +188,46 @@ docker compose -f stacks/manager/compose.yaml --env-file stacks/manager/.env up
|
|||||||
```
|
```
|
||||||
|
|
||||||
If both the business app and Manager changed, do both update steps.
|
If both the business app and Manager changed, do both update steps.
|
||||||
|
|
||||||
|
## Upgrading from a version that included Dockge (one-time)
|
||||||
|
|
||||||
|
Dockge used to run alongside NextChair Manager as a separate stack
|
||||||
|
management UI. It's no longer part of the shared infrastructure stack —
|
||||||
|
Manager is the only tool needed to provision and manage business stacks,
|
||||||
|
so the two were redundant. If your server was set up before this change,
|
||||||
|
existing tenant databases, tenant containers, business Compose files, and
|
||||||
|
the global SMTP/VAPID settings are all unaffected — this only removes the
|
||||||
|
old Dockge container.
|
||||||
|
|
||||||
|
Pull the change, then re-apply the infra stack with `--remove-orphans` so
|
||||||
|
Docker removes the container that's no longer defined in
|
||||||
|
`stacks/infra/compose.yaml`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/nextchair
|
||||||
|
git pull
|
||||||
|
|
||||||
|
docker compose \
|
||||||
|
-f stacks/infra/compose.yaml \
|
||||||
|
--env-file stacks/infra/.env \
|
||||||
|
up -d --remove-orphans
|
||||||
|
```
|
||||||
|
|
||||||
|
`--remove-orphans` only removes containers Compose no longer recognizes
|
||||||
|
(the old `next-chair-dockge` container) — it does not touch Caddy, MySQL,
|
||||||
|
or their volumes.
|
||||||
|
|
||||||
|
This does **not** delete Dockge's own data volume
|
||||||
|
(`next-chair-dockge-data`) — Docker never removes volumes on its own, and
|
||||||
|
this command has no reason to either. Once you've confirmed Caddy, MySQL
|
||||||
|
and every business are still healthy, you can remove that now-unused
|
||||||
|
volume by hand:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker volume rm next-chair-dockge-data
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, `STACKS_DIR` and `DOCKGE_HOST_PORT` in `stacks/infra/.env` were
|
||||||
|
only ever used by Dockge and are safe to delete from that file — nothing
|
||||||
|
else in the infra stack reads them. (Manager keeps its own separate
|
||||||
|
`STACKS_DIR` in `stacks/manager/.env`; leave that one alone.)
|
||||||
@@ -29,8 +29,12 @@ MYSQL_PORT="3306"
|
|||||||
MYSQL_ROOT_PASSWORD="replace-me"
|
MYSQL_ROOT_PASSWORD="replace-me"
|
||||||
|
|
||||||
# ── Docker / stacks ──────────────────────────────────────────────────────
|
# ── Docker / stacks ──────────────────────────────────────────────────────
|
||||||
# Must be the identical absolute path as stacks/infra/.env's STACKS_DIR —
|
# This repo's absolute path on the VPS, e.g. "/opt/nextchair/stacks" — NOT
|
||||||
# see that file's comment for why.
|
# a relative path. This container shells `docker compose` out to the
|
||||||
|
# HOST's dockerd via the mounted Docker socket (see src/lib/docker.ts), so
|
||||||
|
# a compose-file path only resolves to the same file on both sides if the
|
||||||
|
# bind-mount doesn't change it — meaning this must be mounted at the
|
||||||
|
# identical path inside the container too (see stacks/manager/compose.yaml).
|
||||||
STACKS_DIR="/opt/nextchair/stacks"
|
STACKS_DIR="/opt/nextchair/stacks"
|
||||||
# Every business's compose.yaml (see src/lib/compose-template.ts) uses
|
# Every business's compose.yaml (see src/lib/compose-template.ts) uses
|
||||||
# this image tag — rebuild with `docker build -t nextchair-app:latest .`
|
# this image tag — rebuild with `docker build -t nextchair-app:latest .`
|
||||||
@@ -38,7 +42,3 @@ STACKS_DIR="/opt/nextchair/stacks"
|
|||||||
# "Update" button (or `docker compose up -d --force-recreate`) to pick it
|
# "Update" button (or `docker compose up -d --force-recreate`) to pick it
|
||||||
# up.
|
# up.
|
||||||
NEXTCHAIR_IMAGE="nextchair-app:latest"
|
NEXTCHAIR_IMAGE="nextchair-app:latest"
|
||||||
|
|
||||||
# ── Optional ─────────────────────────────────────────────────────────────
|
|
||||||
# Shown as a link in the dashboard header if set.
|
|
||||||
DOCKGE_URL="http://your-vps-ip:5001"
|
|
||||||
+1
-2
@@ -58,8 +58,7 @@ effectively means root on the host. Consequences:
|
|||||||
both host and container (see `.env.example`) — a `docker compose -f
|
both host and container (see `.env.example`) — a `docker compose -f
|
||||||
<path>` command run from inside this container is executed by the
|
<path>` command run from inside this container is executed by the
|
||||||
*host's* dockerd, so the path only means the same file on both sides if
|
*host's* dockerd, so the path only means the same file on both sides if
|
||||||
the mount doesn't change it. This is the same constraint Dockge itself
|
the mount doesn't change it.
|
||||||
has.
|
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import Link from "next/link";
|
|||||||
import { logoutAction } from "./logout-action";
|
import { logoutAction } from "./logout-action";
|
||||||
|
|
||||||
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
|
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
|
||||||
const dockgeUrl = process.env.DOCKGE_URL;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen">
|
<div className="min-h-screen">
|
||||||
<header className="flex items-center justify-between border-b border-line bg-surface px-6 py-4">
|
<header className="flex items-center justify-between border-b border-line bg-surface px-6 py-4">
|
||||||
@@ -14,11 +12,6 @@ export default function DashboardLayout({ children }: { children: React.ReactNod
|
|||||||
<Link href="/dashboard/settings" className="text-muted hover:text-ink">
|
<Link href="/dashboard/settings" className="text-muted hover:text-ink">
|
||||||
Settings
|
Settings
|
||||||
</Link>
|
</Link>
|
||||||
{dockgeUrl && (
|
|
||||||
<a href={dockgeUrl} target="_blank" rel="noreferrer" className="text-muted hover:text-ink">
|
|
||||||
Dockge ↗
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
<form action={logoutAction}>
|
<form action={logoutAction}>
|
||||||
<button type="submit" className="text-muted hover:text-ink">
|
<button type="submit" className="text-muted hover:text-ink">
|
||||||
Sign out
|
Sign out
|
||||||
|
|||||||
@@ -7,9 +7,8 @@ const execFileAsync = promisify(execFile);
|
|||||||
* Every call here runs `docker compose` with `cwd` set to a stack's own
|
* Every call here runs `docker compose` with `cwd` set to a stack's own
|
||||||
* directory (stacks/<slug>/) rather than passing explicit -f/--env-file
|
* directory (stacks/<slug>/) rather than passing explicit -f/--env-file
|
||||||
* flags — compose already auto-loads `compose.yaml` and `.env` from the
|
* flags — compose already auto-loads `compose.yaml` and `.env` from the
|
||||||
* current directory, and this is the same convention Dockge itself uses,
|
* current directory, so a stack behaves identically whether it's started
|
||||||
* so a stack behaves identically whether it's started by this app, by
|
* by this app or by hand from the command line.
|
||||||
* Dockge, or by hand from the command line.
|
|
||||||
*
|
*
|
||||||
* This app's own container needs the Docker socket mounted and the
|
* This app's own container needs the Docker socket mounted and the
|
||||||
* `docker compose` CLI installed (see manager/Dockerfile) — these calls
|
* `docker compose` CLI installed (see manager/Dockerfile) — these calls
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
name: next-chair-blake-heritage
|
name: next-chair-blake-heritage
|
||||||
|
|
||||||
# One business's app container on the shared multi-business VPS setup —
|
# One business's app container on the shared multi-business VPS setup —
|
||||||
# see stacks/infra/compose.yaml for the shared MySQL/Caddy/Dockge this
|
# see stacks/infra/compose.yaml for the shared MySQL/Caddy this attaches
|
||||||
# attaches to, and that file's header for the exact one-time setup steps
|
# to, and that file's header for the exact one-time setup steps
|
||||||
# (create this business's database, copy .env.business.example, add a
|
# (create this business's database, copy .env.business.example, add a
|
||||||
# Caddy site file) needed before this file can start.
|
# Caddy site file) needed before this file can start.
|
||||||
#
|
#
|
||||||
|
|||||||
+11
-35
@@ -1,24 +1,24 @@
|
|||||||
name: next-chair-infra
|
name: next-chair-infra
|
||||||
|
|
||||||
# Shared infrastructure for the multi-business VPS deployment: Caddy
|
# Shared infrastructure for the multi-business VPS deployment: Caddy
|
||||||
# (reverse proxy + automatic TLS via Let's Encrypt), one MySQL server
|
# (reverse proxy + automatic TLS via Let's Encrypt) and one MySQL server
|
||||||
# shared by every business (a separate database + user per business, NOT
|
# shared by every business (a separate database + user per business, NOT
|
||||||
# one shared schema — see step 1 below), and Dockge (a web UI for
|
# one shared schema — see step 1 below).
|
||||||
# starting/stopping/inspecting these compose stacks by hand).
|
|
||||||
#
|
#
|
||||||
# Lives in stacks/infra/ so Dockge (which expects one directory per
|
# Lives in stacks/infra/, one directory per stack (each with its own
|
||||||
# stack, each with its own compose.yaml) can manage this stack too —
|
# compose.yaml) — see stacks/blake-heritage/, stacks/marios/ for the same
|
||||||
# see stacks/blake-heritage/, stacks/marios/ for the same shape per
|
# shape per business, and stacks/manager/ for the management UI's own
|
||||||
# business. Deliberately separate from docker-compose.local.yaml at the
|
# stack. Deliberately separate from docker-compose.local.yaml at the
|
||||||
# repo root, which is the single-business local/Docker-Desktop setup and
|
# repo root, which is the single-business local/Docker-Desktop setup and
|
||||||
# stays untouched by any of this.
|
# stays untouched by any of this.
|
||||||
#
|
#
|
||||||
|
# Stack management/provisioning is handled by NextChair Manager (see
|
||||||
|
# manager/ at the repo root) — there is no separate stack-management UI
|
||||||
|
# alongside it.
|
||||||
|
#
|
||||||
# ── One-time setup ───────────────────────────────────────────────────────
|
# ── One-time setup ───────────────────────────────────────────────────────
|
||||||
# docker network create next-chair-edge
|
# docker network create next-chair-edge
|
||||||
# cp .env.infra.example stacks/infra/.env (then fill in real values —
|
# cp .env.infra.example stacks/infra/.env (then fill in real values)
|
||||||
# in particular STACKS_DIR must be this repo's absolute path on the
|
|
||||||
# VPS, e.g. /opt/nextchair/stacks — see the STACKS_DIR comment in that
|
|
||||||
# file for why it can't just be a relative path here)
|
|
||||||
# docker compose -f stacks/infra/compose.yaml --env-file stacks/infra/.env up -d
|
# docker compose -f stacks/infra/compose.yaml --env-file stacks/infra/.env up -d
|
||||||
#
|
#
|
||||||
# ── Per business ──────────────────────────────────────────────────────
|
# ── Per business ──────────────────────────────────────────────────────
|
||||||
@@ -98,28 +98,6 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- next-chair-edge
|
- next-chair-edge
|
||||||
|
|
||||||
dockge:
|
|
||||||
image: louislam/dockge:1
|
|
||||||
container_name: next-chair-dockge
|
|
||||||
restart: unless-stopped
|
|
||||||
env_file:
|
|
||||||
- .env
|
|
||||||
environment:
|
|
||||||
DOCKGE_STACKS_DIR: "${STACKS_DIR}"
|
|
||||||
ports:
|
|
||||||
- "${DOCKGE_HOST_PORT:-5001}:5001"
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
|
||||||
- next-chair-dockge-data:/app/data
|
|
||||||
# MUST be mounted at the identical absolute path on both host and
|
|
||||||
# container — see the STACKS_DIR comment in .env.infra.example for
|
|
||||||
# why (Dockge shells `docker compose` out to the HOST's dockerd via
|
|
||||||
# the socket above, so a compose-file path only means the same
|
|
||||||
# thing on both sides if the bind-mount doesn't change it).
|
|
||||||
- "${STACKS_DIR}:${STACKS_DIR}"
|
|
||||||
networks:
|
|
||||||
- next-chair-edge
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
next-chair-mysql-data:
|
next-chair-mysql-data:
|
||||||
name: next-chair-mysql-data
|
name: next-chair-mysql-data
|
||||||
@@ -127,8 +105,6 @@ volumes:
|
|||||||
name: next-chair-caddy-data
|
name: next-chair-caddy-data
|
||||||
next-chair-caddy-config:
|
next-chair-caddy-config:
|
||||||
name: next-chair-caddy-config
|
name: next-chair-caddy-config
|
||||||
next-chair-dockge-data:
|
|
||||||
name: next-chair-dockge-data
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
next-chair-edge:
|
next-chair-edge:
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ name: next-chair-manager
|
|||||||
# explains why it's a fully separate app). Its own stack, separate from
|
# explains why it's a fully separate app). Its own stack, separate from
|
||||||
# stacks/infra/compose.yaml, since this is real application code that
|
# stacks/infra/compose.yaml, since this is real application code that
|
||||||
# gets rebuilt/updated far more often than the off-the-shelf infra
|
# gets rebuilt/updated far more often than the off-the-shelf infra
|
||||||
# images (MySQL, Caddy, Dockge) — a code change here shouldn't require
|
# images (MySQL, Caddy) — a code change here shouldn't require touching
|
||||||
# touching the infra stack at all.
|
# the infra stack at all.
|
||||||
#
|
#
|
||||||
# cp manager/.env.example stacks/manager/.env (then fill in real values)
|
# cp manager/.env.example stacks/manager/.env (then fill in real values)
|
||||||
# docker compose -f stacks/manager/compose.yaml --env-file stacks/manager/.env up -d --build
|
# docker compose -f stacks/manager/compose.yaml --env-file stacks/manager/.env up -d --build
|
||||||
|
|||||||
Reference in new issue
Block a user