Production cutover — static IP + SSL (hydrotrek.org)
Server: msi-laptop-3
Public static IP (Ethernet): 74.83.141.228
TLS hostnames: hydrotrek.org, www.hydrotrek.org
Client stack prefix: gqc
ASP.NET data API (unchanged): https://www.hydrotrek.xyz/api/
Cert files (installed):
| File | Path |
|---|---|
| Full chain | /etc/ssl/hydrotrek/hydrotrek.org-fullchain.crt |
| Private key | /etc/ssl/hydrotrek/hydrotrek.org.key |
URL map (browser / frontend)
Use hostname URLs in production (cert matches). Raw IP HTTPS will warn (cert has no IP SAN).
| Service | Production URL |
|---|---|
| FastAPI health | https://www.hydrotrek.org/gqc/api/fastapi/health |
| Swagger | https://www.hydrotrek.org/gqc/api/fastapi/docs |
| Graph data | https://www.hydrotrek.org/gqc/api/fastapi/graph-data |
| CopilotKit | https://www.hydrotrek.org/gqc/api/copilotkit |
| MCP (protocol) | https://www.hydrotrek.org/gqc/api/mcp |
| ASP.NET v2 | https://www.hydrotrek.xyz/api/ |
Do not use http://74.83.141.228/... or https://74.83.141.228/... in frontend env — use www.hydrotrek.org.
Docker server-to-server (unchanged — no public IP):
| From | Env | Value |
|---|---|---|
dwd-copilot-server | AGUI_AGENT_URL | http://dwd-api-fastapi:8080/agent |
dwd-api-fastapi | MCP_SERVER_URL | http://dwd-mcp-fastmcp:8081/mcp |
Phase 1 — Network
DNS (registrar)
| Record | Type | Value |
|---|---|---|
hydrotrek.org | A | 74.83.141.228 |
www.hydrotrek.org | A or CNAME | 74.83.141.228 or hydrotrek.org |
Verify:
dig +short hydrotrek.org A
dig +short www.hydrotrek.org A
Netplan (Ethernet static on enp58s0)
Confirm the host owns 74.83.141.228 on Ethernet (see 09-wifi-and-ssh-network-setup.md). Example shape:
# /etc/netplan/50-cloud-init.yaml (values from ISP — gateway/mask may differ)
addresses: [74.83.141.228/XX]
gateway4: <isp-gateway>
sudo netplan apply
ip -4 addr show enp58s0
UFW (Ethernet = public API)
sudo ufw allow in on enp58s0 to any port 80 proto tcp comment 'HTTP hydrotrek.org'
sudo ufw allow in on enp58s0 to any port 443 proto tcp comment 'HTTPS hydrotrek.org'
# SSH on wlo1 only — see doc 09
sudo ufw enable # when ready
sudo ufw status
No inbound rules for 8001–8003 (containers stay on 127.0.0.1).
Phase 2 — Nginx HTTPS
Edit /etc/nginx/sites-available/dwd-gqc.
- Add
listen 443 sslserver block withserver_name hydrotrek.org www.hydrotrek.organd the same/gqc/api/...locations as port 80. - SSL paths:
ssl_certificate /etc/ssl/hydrotrek/hydrotrek.org-fullchain.crt;
ssl_certificate_key /etc/ssl/hydrotrek/hydrotrek.org.key;
ssl_protocols TLSv1.2 TLSv1.3;
- CopilotKit: proxy
location = /gqc/api/copilotkit— noreturn 301(CORS preflight). - Optional HTTP → HTTPS for public hostnames only:
server {
listen 80;
listen [::]:80;
server_name hydrotrek.org www.hydrotrek.org;
return 301 https://$host$request_uri;
}
- Optional keep LAN/dev HTTP on IP (cert mismatch if you use HTTPS on IP):
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
# same /gqc/api/... locations for http://74.83.141.228/... (HTTP only)
}
Apply:
sudo nginx -t
sudo systemctl reload nginx
Smoke tests:
curl -sS https://www.hydrotrek.org/gqc/api/fastapi/health
curl -sS -o /dev/null -w "%{http_code}\n" https://www.hydrotrek.org/gqc/api/fastapi/openapi.json
Phase 3 — Docker env files
/etc/dwd-api-fastapi/dwd-api-fastapi.env
OPENAI_API_KEY=...
OPENAI_CHAT_MODEL_ID=gpt-4o-mini
DWD_API_BASE_URL=https://www.hydrotrek.xyz/api/
ROOT_PATH=/gqc/api/fastapi
/etc/dwd-mcp-fastmcp/dwd-mcp-fastmcp.env
DWD_API_BASE_URL=https://www.hydrotrek.xyz
/etc/dwd-copilot-server/dwd-copilot-server.env
COPILOTKIT_TELEMETRY_DISABLED=true
CORS_EXTRA_ORIGINS=https://www.hydrotrek.org,https://hydrotrek.org,http://localhost:3000
Compose (/etc/dwd-stack/docker-compose.yml) keeps:
MCP_SERVER_URL: http://dwd-mcp-fastmcp:8081/mcp
AGUI_AGENT_URL: http://dwd-api-fastapi:8080/agent
Recreate after changes:
cd /etc/dwd-stack
sudo docker compose up -d --build --force-recreate
curl -s http://127.0.0.1:8001/health | python3 -m json.tool
Phase 4 — CORS (FastAPI code)
Add production SPA origins to repos/dwd-api-fastapi/config.py → CORS_ORIGINS:
"https://www.hydrotrek.org",
"https://hydrotrek.org",
Rebuild dwd-api-fastapi image on server after pull.
Phase 5 — Frontend (Vite / production build)
VITE_API_URL=https://www.hydrotrek.xyz/
VITE_FASTAPI_URL=https://www.hydrotrek.org/gqc/api/fastapi/
VITE_COPILOT_RUNTIME_URL=https://www.hydrotrek.org/gqc/api/copilotkit
Restart dev server after .env change. Trailing slash on VITE_FASTAPI_URL only.
Local dev can still use http://localhost:3000 → https://www.hydrotrek.org/gqc/api/... (CORS must allow localhost:3000).
Phase 6 — Verification checklist
[ ] dig hydrotrek.org → 74.83.141.228
[ ] enp58s0 has 74.83.141.228
[ ] UFW 80/443 on enp58s0; 8001–8003 not public
[ ] nginx 443 + cert paths
[ ] https://www.hydrotrek.org/gqc/api/fastapi/health → 200
[ ] https://www.hydrotrek.org/gqc/api/fastapi/docs → Swagger loads
[ ] ROOT_PATH in /health JSON
[ ] Copilot OPTIONS/POST from localhost:3000
[ ] Frontend VITE_* use www.hydrotrek.org (not IP)
[ ] Containers use Docker DNS only (no 74.83.141.228 in compose env)