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/copilotkitno 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.pyCORS_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:3000https://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)