You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -23,54 +23,107 @@ Access the dashboard at `http://your-server-ip:3000`.
23
23
24
24
### 1.2 DNS Configuration (Cloudflare)
25
25
26
+
We recommend **Cloudflare** (free tier) for DNS because Traefik's wildcard SSL requires DNS-01 challenge, and Cloudflare has first-class integration. If you're currently on GoDaddy or another registrar, migrate your nameservers to Cloudflare:
27
+
28
+
1. Create a free account at [dash.cloudflare.com](https://dash.cloudflare.com)
29
+
2.**Add your domain** — Cloudflare will scan existing DNS records
30
+
3. Cloudflare gives you two nameservers (e.g., `karina.ns.cloudflare.com`, `karl.ns.cloudflare.com`)
31
+
4.**Update nameservers at your registrar** (e.g., GoDaddy > My Domains > DNS > Nameservers > Custom) to point to Cloudflare's nameservers
**Critical:** The wildcard `*` record MUST be **DNS only (grey cloud)** so Traefik handles SSL directly. If proxied through Cloudflare, the wildcard cert won't work with Traefik's DNS challenge.
42
+
**Critical:** Both `@` and `*` records MUST be **DNS only (grey cloud)** so Traefik handles SSL directly. If proxied through Cloudflare, the wildcard cert won't work with Traefik's DNS challenge. You can enable Cloudflare proxy on the `@` record later once everything is working.
43
+
44
+
**Clean up old records:** If you migrated from another provider, delete any leftover A records pointing to old IPs (e.g., GoDaddy forwarding IPs). You should have exactly ONE A record per name.
33
45
34
46
### 1.3 Cloudflare API Token
35
47
36
-
1. Cloudflare Dashboard > My Profile > API Tokens
37
-
2. Create Token > **Edit zone DNS** template
38
-
3. Zone Resources: Include your domain
39
-
4. Save the token for Traefik configuration
48
+
1. Go to [dash.cloudflare.com/profile/api-tokens](https://dash.cloudflare.com/profile/api-tokens)
49
+
2. Click **"Create Token"**
50
+
3. Use the **"Edit zone DNS"** template
51
+
4. Zone Resources: **Include > Specific zone > your domain**
52
+
5. Click **Continue to summary > Create Token**
53
+
6.**Copy and save the token** — you'll need it for Traefik configuration
54
+
55
+
> **Security:** Never share this token publicly. If compromised, rotate it immediately from the same page.
40
56
41
57
---
42
58
43
59
## 2. Traefik Wildcard SSL
44
60
45
61
This is what makes multi-tenant subdomains work with HTTPS.
46
62
47
-
### 2.1 Add Cloudflare Token
63
+
### 2.1 Add Cloudflare Token to Traefik
48
64
49
-
In Dokploy: **Web Server > Traefik > Environment**
65
+
In Dokploy: **Web Server** (left sidebar) > scroll to **Web Server** section > click **Traefik** button > select **"Modify Environment"** from the dropdown:
50
66
51
67
```
52
68
CF_DNS_API_TOKEN=your-cloudflare-api-token
53
69
```
54
70
71
+
Save the environment.
72
+
55
73
### 2.2 Add DNS Challenge Resolver
56
74
57
-
In Dokploy: **Web Server > Traefik > traefik.yml** — add a DNS challenge resolver alongside the existing one:
75
+
In Dokploy: **Traefik File System** (left sidebar) > open the `traefik.yml` file.
76
+
77
+
Add a `letsencrypt-wildcard` DNS challenge resolver **alongside** the existing `letsencrypt` HTTP challenge resolver. Your full `traefik.yml` should look like:
Save the file, then go back to **Web Server** and click **Traefik > Reload** to apply changes.
124
+
125
+
> **Note:** The `letsencrypt` resolver (HTTP challenge) is used for the root domain. The `letsencrypt-wildcard` resolver (DNS challenge via Cloudflare) is used for `*.yourdomain.com`. Both coexist.
In the app's **Advanced > Traefik** section, add custom labels:
200
+
> **Important:** Do NOT add `*.lmsplatform.com` as a regular domain in Dokploy — Traefik does not support literal `*` in `Host()` rules and will error with: `"*.lmsplatform.com" is not a valid hostname`. You must use `HostRegexp` via the Advanced config.
201
+
202
+
In the app's **Advanced** tab, find the Traefik dynamic configuration. You'll see the existing router config for your root domain. Add wildcard routers alongside it.
203
+
204
+
Here's what the full Advanced config should look like (replace `YOUR-SERVICE-NAME` with the Dokploy-generated service name visible in the existing config, and `YOUR-APP-CONTAINER` with the container hostname):
Replace `<SERVICE_HASH>` with the Dokploy-generated service name (visible in the Traefik dashboard or via `docker inspect`).
255
+
**How to find `YOUR-SERVICE-NAME` and `YOUR-APP-CONTAINER`:** Look at the existing config in the Advanced tab — Dokploy auto-generates these when you add the root domain. They follow the pattern `project-name-hash-service-N` (e.g., `guille-personal-lms-lj9e61-service-15`).
256
+
257
+
After saving, **redeploy** the app. Allow 1-2 minutes for Let's Encrypt to issue the wildcard certificate via DNS challenge.
163
258
164
259
### 3.5 Cron Jobs
165
260
@@ -313,8 +408,70 @@ After deployment, verify:
313
408
314
409
---
315
410
411
+
## 8. Troubleshooting
412
+
413
+
### Wildcard subdomain returns 404
414
+
415
+
**Symptom:** `school.lmsplatform.com` returns a Traefik 404 page, but `lmsplatform.com` works.
416
+
417
+
**Causes and fixes:**
418
+
419
+
1. **Missing wildcard router:** Check that the `lms-wildcard` and `lms-wildcard-secure` routers exist in the Advanced config. Dokploy's regular domain UI does not support wildcards.
420
+
421
+
2. **Traefik error `"*.domain.com" is not a valid hostname`:** You added `*.domain.com` as a regular domain in Dokploy's Domains tab. Delete it — wildcards must use `HostRegexp` in the Advanced config.
422
+
423
+
3. **DNS not resolving:** Run `dig +short school.lmsplatform.com` — it should return your server IP. If not, check that the `*` A record exists in Cloudflare and nameservers have propagated.
424
+
425
+
4. **Wildcard cert not issued:** Check Traefik logs (**Web Server > Traefik > View Logs**) for ACME errors. Common causes:
426
+
- `CF_DNS_API_TOKEN`not set in Traefik environment
427
+
- Cloudflare API token doesn't have `DNS:Edit` permission on the correct zone
428
+
- `letsencrypt-wildcard`resolver not defined in `traefik.yml`
429
+
- `acme-wildcard.json`storage path conflict — ensure it's different from the HTTP challenge storage
430
+
431
+
5. **Cloudflare proxy (orange cloud) enabled on wildcard:** The `*` A record must be **DNS only (grey cloud)**. Cloudflare proxy intercepts TLS and breaks Traefik's DNS challenge.
432
+
433
+
### Tenant not created after onboarding
434
+
435
+
**Symptom:** User completes the create-school flow but the subdomain shows "Invalid tenant."
436
+
437
+
**Cause:** The `create_school` RPC requires an authenticated user (`auth.uid()` must not be null). If the user's session expired or cookies weren't sent, the RPC fails silently on the client.
438
+
439
+
**Fix:** Check the browser console for RPC errors. The user should log in again and retry the create-school flow. Alternatively, create the tenant manually via SQL:
440
+
441
+
```sql
442
+
-- Create tenant
443
+
INSERT INTO tenants (name, slug, status)
444
+
VALUES ('School Name', 'school-slug', 'active')
445
+
RETURNING id;
446
+
447
+
-- Add admin user (replace UUIDs)
448
+
INSERT INTO tenant_users (tenant_id, user_id, role, status)
Traefik auto-renews Let's Encrypt certificates. If renewal fails:
455
+
456
+
1. Check Traefik logs for ACME errors
457
+
2. Verify the Cloudflare API token hasn't been revoked
458
+
3. Ensure the `acme-wildcard.json` file is writable
459
+
4. Try deleting the `acme-wildcard.json` file and reloading Traefik to force re-issuance
460
+
461
+
---
462
+
316
463
## Recommendation
317
464
318
465
**Start with Supabase Cloud** to avoid managing Postgres backups, Auth configuration, and the custom access token hook. Migrate to self-hosted later once traffic justifies it.
319
466
320
467
The critical work is in **Sections 2-3**: getting Traefik wildcard SSL + DNS challenge working. Once that's done, subdomain routing works automatically via `proxy.ts`.
0 commit comments