Reconfigure Domain¶
deployment/reconfigure-domain.sh re-runs the domain configuration on an existing install without re-installing the stack. Use it when the initial deployment/server-setup.sh ran without a domain (NO_TUNNEL=1 HOST=<ip>), or when you change the public domain after the fact. The script rewrites every host-bearing env var in .env, rebuilds apps/web (because NEXT_PUBLIC_* are baked at build time), and restarts doable.service.
The script is idempotent. Running it twice with the same DOMAIN is a no-op after the first pass.
When to use it¶
- The original install was
NO_TUNNEL=1 HOST=<ip>, so.envcarriesCORS_ORIGINS=https://<ip>,NEXT_PUBLIC_*=https://<ip>, and so on, and you now want the install to publish at a real domain. - You moved from one domain to another, or from staging to production.
- You added a Cloudflare ACM cert and want to flip from the prefix layout (
<env>-slug.zone) to the infix layout (slug.<env>.zone).
Prerequisites¶
- An existing Doable install on the server, typically at
/root/doable(override with--install-dir <path>orINSTALL_DIR=). - Write permission on
<INSTALL_DIR>/.env. The script errors out if the file is missing or read-only. - The DOMAIN you pass must already have DNS pointed at the Cloudflare Tunnel (otherwise the smoke test at the end will report 530s but the env rewrite still succeeds).
Usage¶
# Env-style
DOMAIN=acme.example.com ./deployment/reconfigure-domain.sh
# Flag-style
./deployment/reconfigure-domain.sh --domain dev.doable.me
# Multi-level zones work too
./deployment/reconfigure-domain.sh --domain staging.doable.me
Cloudflare-friendly subdomain layout¶
The script derives the API and WS hostnames from DOMAIN using Doable's Cloudflare-naming rule, which exists because free Universal SSL covers <zone> and *.<zone> only (one-level wildcard).
| DOMAIN | API hostname | WS hostname | Layout |
|---|---|---|---|
acme.example.com (apex) |
api.acme.example.com |
ws.acme.example.com |
Dotted |
dev.doable.me (multi-level) |
dev-api.doable.me |
dev-ws.doable.me |
Dashed |
Override per call if needed:
./deployment/reconfigure-domain.sh \
--domain dev.doable.me \
--api-domain api-dev.doable.me \
--ws-domain ws-dev.doable.me
Publish layout: prefix vs infix¶
Published apps default to a single-level wildcard (prefix layout): <prefix><slug>.<zone>. With Cloudflare ACM you can opt into the infix layout (<slug>.<env>.<zone>):
# Prefix (default, free Universal SSL): https://dev-myapp-abc.doable.me
./deployment/reconfigure-domain.sh --domain dev.doable.me --layout prefix
# Infix (requires Cloudflare ACM): https://myapp-abc.dev.doable.me
./deployment/reconfigure-domain.sh --domain dev.doable.me \
--layout infix --wildcard-hostname '*.dev.doable.me'
Tip: if PUBLISH_LAYOUT already exists in .env, the script keeps that value unless --layout overrides it.
Flag reference¶
| Flag | Default | Notes |
|---|---|---|
--domain <zone> |
$DOMAIN env |
Required if env not set. |
--install-dir <path> |
/root/doable or $INSTALL_DIR |
Where .env lives. |
--api-domain <host> |
Derived from DOMAIN | Override the computed API host. |
--ws-domain <host> |
Derived from DOMAIN | Override the computed WS host. |
--layout prefix\|infix |
Existing or prefix |
Sets PUBLISH_LAYOUT. |
--wildcard-hostname '*.zone' |
*.<domain> (infix only) |
Must start with *. and live inside the zone. |
--publish-prefix <s> |
Existing or derived | Overrides PUBLISH_SUBDOMAIN_PREFIX. |
--no-rebuild |
false | Skip pnpm --filter @doable/web build. The running bundle still has the OLD NEXT_PUBLIC_* baked in until you rebuild. |
--no-restart |
false | Skip systemctl restart doable.service. |
--dry-run |
false | Print the diff but write nothing. |
What it rewrites¶
Every host-bearing env var in <INSTALL_DIR>/.env is rewritten in place. A timestamped backup is created at <INSTALL_DIR>/.env.pre-domain-YYYYMMDD-HHMMSS.
Variables touched:
NEXT_PUBLIC_APP_URL https://<domain>
NEXT_PUBLIC_API_URL https://<api-domain>
NEXT_PUBLIC_WS_URL wss://<ws-domain>
CORS_ORIGINS https://<domain>
WS_ALLOWED_ORIGINS https://<domain>
DOABLE_DOMAIN <publish-apex>
PUBLISH_LAYOUT prefix | infix
PUBLISH_SUBDOMAIN_PREFIX <prefix>
WILDCARD_HOSTNAME *.<wildcard-bare>
GOOGLE_REDIRECT_URI https://<api-domain>/auth/google/callback
GITHUB_REDIRECT_URI https://<api-domain>/oauth/github/login/callback
GITHUB_COPILOT_REDIRECT_URI https://<api-domain>/oauth/github/copilot/callback
GITHUB_REPO_REDIRECT_URI https://<api-domain>/oauth/github/repo/callback
INTEGRATIONS_OAUTH_REDIRECT_URI https://<api-domain>/integrations/oauth/callback
INTEGRATIONS_ENHANCED_AUTH_REDIRECT_URI https://<api-domain>/integrations/enhanced-auth/callback
The script also writes apps/web/.env.local with the new NEXT_PUBLIC_* values to close a Next.js .env precedence trap: a stale .env.local would otherwise silently outvote the rewrites at build time.
Build + restart¶
By default the script:
- Backs up
.envto.env.pre-domain-YYYYMMDD-HHMMSS. - Rewrites every key above in place.
- Syncs
apps/web/.env.local. - Runs
env -u NODE_ENV NODE_ENV=production pnpm --filter @doable/web buildas the install owner (3 to 6 minutes). - Runs
systemctl restart doable.service. - Smoke-tests
http://127.0.0.1:{3000,4000,4001}plus the public URLs.
Skip steps 4 and 5 with --no-rebuild and --no-restart respectively, but remember that the running web bundle still has the OLD URLs baked in until you rebuild.
Smoke test output¶
After it runs, the script prints something like:
api (127.0.0.1:4000/health): 200
web (127.0.0.1:3000/): 200
ws (127.0.0.1:4001/health): 200
Public hostnames (DNS + TLS must be live for these to pass):
https://dev.doable.me/ -> 200
https://dev-api.doable.me/health -> 200
https://dev-ws.doable.me/health -> 200
If the public URLs return 530, the Cloudflare Tunnel routes are missing. Add them:
cloudflared tunnel route dns --overwrite-dns <tunnel-uuid> dev.doable.me
cloudflared tunnel route dns --overwrite-dns <tunnel-uuid> dev-api.doable.me
cloudflared tunnel route dns --overwrite-dns <tunnel-uuid> dev-ws.doable.me
Diffing the rewrite¶
Troubleshooting¶
DOMAIN is required. Set DOMAIN= in the environment or pass --domain <zone>.
.env not found. Confirm --install-dir points at a real Doable install. By default the script looks at /root/doable.
.env is not writable. Run as the install owner (typically root) or chown to the right user.
Web still serves the old URLs. Either the build step was skipped (--no-rebuild) or systemctl restart doable.service did not pick up the new bundle. Run the script again without those flags.
Reference¶
- Script:
deployment/reconfigure-domain.sh - Cloudflare naming rule: see Custom Domains and Production
- Related: Production