Skip to main content

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):

FilePath
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).

ServiceProduction URL
FastAPI healthhttps://www.hydrotrek.org/gqc/api/fastapi/health
Swaggerhttps://www.hydrotrek.org/gqc/api/fastapi/docs
Graph datahttps://www.hydrotrek.org/gqc/api/fastapi/graph-data
CopilotKithttps://www.hydrotrek.org/gqc/api/copilotkit
MCP (protocol)https://www.hydrotrek.org/gqc/api/mcp
ASP.NET v2https://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):

FromEnvValue
dwd-copilot-serverAGUI_AGENT_URLhttp://dwd-api-fastapi:8080/agent
dwd-api-fastapiMCP_SERVER_URLhttp://dwd-mcp-fastmcp:8081/mcp

Phase 1 — Network​

DNS (registrar)​

RecordTypeValue
hydrotrek.orgA74.83.141.228
www.hydrotrek.orgA or CNAME74.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.

  1. Add listen 443 ssl server block with server_name hydrotrek.org www.hydrotrek.org and the same /gqc/api/... locations as port 80.
  2. 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;
  1. CopilotKit: proxy location = /gqc/api/copilotkit — no return 301 (CORS preflight).
  2. 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;
}
  1. 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)