From 6709d2ef1d9c6159326b38556b5965274db184f6 Mon Sep 17 00:00:00 2001 From: Gitead Date: Fri, 31 Jul 2026 19:14:47 -0400 Subject: [PATCH] Add deployment README for app.mydentalofficemanagement.com Documents the nginx + Cloudflare Tunnel setup so it can be reproduced on a new Debian machine. --- artifacts/dental-app/README.md | 228 +++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 artifacts/dental-app/README.md diff --git a/artifacts/dental-app/README.md b/artifacts/dental-app/README.md new file mode 100644 index 0000000..49aee21 --- /dev/null +++ b/artifacts/dental-app/README.md @@ -0,0 +1,228 @@ +# Deployment: app.mydentalofficemanagement.com + +How this app is served in production: nginx serves a static production +build on this machine, and Cloudflare Tunnel exposes it publicly at +`https://app.mydentalofficemanagement.com` without opening any ports on +the router. + +``` +Browser → Cloudflare (DNS + edge) → Tunnel (cloudflared, systemd) → nginx (:80) → static files +``` + +## 1. Prerequisites + +- Debian (tested on Debian 13 "trixie") +- sudo access +- Node.js + pnpm installed, repo cloned +- The domain `mydentalofficemanagement.com` already added as a zone in + Cloudflare, with a Cloudflare account you can log into + +## 2. Install cloudflared + +Debian's codename is usually too new for Cloudflare's apt repo, so install +the `.deb` package directly instead: + +```bash +curl -fsSL -o /tmp/cloudflared.deb \ + https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb +sudo dpkg -i /tmp/cloudflared.deb +cloudflared --version +``` + +## 3. Authenticate cloudflared + +```bash +cloudflared tunnel login +``` + +Opens a browser — log in and authorize the `mydentalofficemanagement.com` +zone. This writes `~/.cloudflared/cert.pem`. + +## 4. Tunnel + +This project uses an existing dashboard-managed tunnel named +**`mydentalmanagement`** (tunnel ID `027669d4-3d96-4f90-920d-e727cecf3b92`). +It already has other subdomains routed through it, so **reuse it** — don't +create a new tunnel for this app. + +If it doesn't exist yet on a fresh Cloudflare account, create one: + +```bash +cloudflared tunnel create mydentalmanagement +``` + +### Get a run token + +```bash +cloudflared tunnel token mydentalmanagement +``` + +Copy the printed token for the next step. (This token authenticates this +machine to run the tunnel — treat it like a secret, don't commit it.) + +### Install cloudflared as a service + +```bash +sudo cloudflared service install +sudo systemctl enable --now cloudflared +sudo systemctl status cloudflared +``` + +## 5. Cloudflare public hostname route + +In the Cloudflare dashboard: + +**Zero Trust → Networks → Tunnels → mydentalmanagement → Published +application routes → Add a published application route** + +| Field | Value | +|---|---| +| Hostname | `app.mydentalofficemanagement.com` | +| Path | *(leave empty)* | +| Service Type | `HTTP` | +| URL | `localhost:80` | + +Note: `mydentalmanagement` was originally a locally-configured tunnel and +had to be **migrated** to dashboard-managed mode the first time this was +set up (Cloudflare shows a one-time "Migrate" prompt for this). That step +is already done — future public hostname routes can just be added +directly from this tab. + +If you see "A DNS record with this name already exists," a stale +CNAME/A record is occupying that hostname — delete it in **DNS → Records** +first, then re-add the route (this recreates the DNS record correctly as +part of the route). + +## 6. Install nginx + +```bash +sudo apt-get install -y nginx +sudo rm -f /etc/nginx/sites-enabled/default +``` + +If Apache is installed and already bound to port 80, disable it first +(only if nothing else depends on it): + +```bash +sudo systemctl disable --now apache2 +``` + +### nginx site config + +Create `/etc/nginx/sites-available/app.mydentalofficemanagement.com.conf`: + +```nginx +server { + listen 80; + server_name app.mydentalofficemanagement.com; + + root /var/www/app.mydentalofficemanagement.com; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } +} +``` + +```bash +sudo ln -s /etc/nginx/sites-available/app.mydentalofficemanagement.com.conf \ + /etc/nginx/sites-enabled/ +sudo nginx -t && sudo systemctl reload nginx +sudo systemctl enable nginx +``` + +Note: `/var/www/...` is used (not a path under `/home//...`) +because nginx runs as `www-data`, and a typical home directory +(`drwx------`) blocks that user from traversing into it. + +## 7. Build and deploy the app + +`vite.config.ts` requires `PORT` and `BASE_PATH` env vars to even load +(`PORT` isn't actually used by the static build, but the config throws +without it regardless of command). + +```bash +cd "artifacts/dental-app" +PORT=5173 BASE_PATH=/ npm run build +``` + +Deploy the build output: + +```bash +sudo mkdir -p /var/www/app.mydentalofficemanagement.com +sudo cp -r dist/public/* /var/www/app.mydentalofficemanagement.com/ +sudo chown -R www-data:www-data /var/www/app.mydentalofficemanagement.com +``` + +### Redeploying after code changes + +Repeat the build + copy steps above any time the app changes: + +```bash +cd "artifacts/dental-app" +PORT=5173 BASE_PATH=/ npm run build +sudo cp -r dist/public/* /var/www/app.mydentalofficemanagement.com/ +``` + +## 8. Running the site + +By default `nginx` and `cloudflared` are systemd services and can either +auto-start on boot or be started manually. + +**Enable auto-start on boot:** +```bash +sudo systemctl enable nginx +sudo systemctl enable cloudflared +``` + +**Disable auto-start (manual start only):** +```bash +sudo systemctl disable nginx +sudo systemctl disable cloudflared +``` + +**Manually start the site** (needed after every reboot if auto-start is +disabled): +```bash +sudo systemctl start cloudflared +sudo systemctl start nginx +``` + +**Stop the site:** +```bash +sudo systemctl stop nginx +sudo systemctl stop cloudflared +``` + +**Check status:** +```bash +systemctl status nginx cloudflared +curl -I https://app.mydentalofficemanagement.com +``` + +No Node/Vite process is needed to serve the site — it's a static build +served directly by nginx. + +## 9. Local development preview (not the public site) + +To preview changes locally before building/deploying, separate from the +live public site: + +```bash +cd "artifacts/dental-app" +PORT=5173 BASE_PATH=/ npm run dev +``` + +Then open `http://localhost:5173` in a browser on this machine. + +## Troubleshooting + +- **502 from the public URL**: check `sudo journalctl -u cloudflared -n 40` + for the exact error (often a port mismatch between the tunnel's + Published Application Route and what nginx is actually listening on). +- **nginx `502`/`403` locally**: check `sudo tail -n 30 /var/log/nginx/error.log`. +- **`cloudflared tunnel list` shows "no active connection"**: the + systemd service isn't running — `sudo systemctl status cloudflared`. +- **Port 80 already in use**: check `sudo ss -tlnp | grep ':80 '` to see + what's holding it (commonly Apache's default install).