From 141afd53bd5ac4f7fb2406164cc526380068d075 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Apr 2026 05:19:04 +0000 Subject: [PATCH] docs: add Caddy HTTPS deployment guide Agent-Logs-Url: https://github.com/genichin/portfolio-hub/sessions/51569572-076d-4f2a-89ad-157d9a97c42a Co-authored-by: genichin <4282227+genichin@users.noreply.github.com> --- README.md | 20 +++++ docs/deployment.md | 194 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 214 insertions(+) create mode 100644 docs/deployment.md diff --git a/README.md b/README.md index 32c0293..9a9eb10 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,26 @@ uvicorn app.main:app --reload pytest ``` +## Production Deployment (HTTPS with Caddy) + +For production, it is recommended to run PortfolioHub behind [Caddy](https://caddyserver.com/), which handles TLS certificate provisioning automatically. + +A minimal `Caddyfile`: + +``` +your-domain.com { + reverse_proxy localhost:8000 +} +``` + +See **[docs/deployment.md](docs/deployment.md)** for a full guide covering: + +- Caddy installation on Linux/Ubuntu +- CORS configuration (`PORTFOLIOHUB_ALLOWED_ORIGINS`) +- Running `uvicorn` as a systemd service +- Docker Compose setup with a Caddy sidecar container +- Firewall port configuration (80, 443) + ## CLI The repository now includes a `portfoliohub` CLI that mirrors the main portfolio flows from `jsbot-kis`. diff --git a/docs/deployment.md b/docs/deployment.md new file mode 100644 index 0000000..0c5f3c0 --- /dev/null +++ b/docs/deployment.md @@ -0,0 +1,194 @@ +# Production Deployment: HTTPS with Caddy + +This guide describes how to deploy PortfolioHub behind [Caddy](https://caddyserver.com/), a reverse proxy that automatically provisions and renews TLS certificates from Let's Encrypt or ZeroSSL. + +## Prerequisites + +- A Linux/Ubuntu server with a public IP address +- A domain name (e.g., `api.example.com`) with a DNS `A` record pointing to that IP +- Ports **80** and **443** open on your firewall (see [Firewall Setup](#firewall-setup)) + +--- + +## 1. Install Caddy (Ubuntu/Debian) + +```bash +sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl +curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \ + | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg +curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \ + | sudo tee /etc/apt/sources.list.d/caddy-stable.list +sudo apt update +sudo apt install caddy +``` + +Verify the installation: + +```bash +caddy version +``` + +--- + +## 2. Configure the Caddyfile + +Create or edit `/etc/caddy/Caddyfile`: + +``` +your-domain.com { + reverse_proxy localhost:8000 +} +``` + +Replace `your-domain.com` with your actual domain. Caddy automatically: + +- Obtains and renews TLS certificates from Let's Encrypt/ZeroSSL +- Redirects plain HTTP (port 80) to HTTPS (port 443) +- Forwards all requests to the PortfolioHub `uvicorn` process on port 8000 + +Reload Caddy after editing the Caddyfile: + +```bash +sudo systemctl reload caddy +``` + +--- + +## 3. CORS Configuration + +When PortfolioHub is accessed from a browser-based frontend over HTTPS, the `PORTFOLIOHUB_ALLOWED_ORIGINS` environment variable must list the exact HTTPS origin(s) of your frontend. + +Set the variable in your environment or `.env` file: + +```bash +PORTFOLIOHUB_ALLOWED_ORIGINS=https://app.example.com,https://www.example.com +``` + +Multiple origins are comma-separated. For the CORS policy details see section 12 of `docs/portfoliohub-spec.md`. + +--- + +## 4. Start PortfolioHub for Production + +Run `uvicorn` without `--reload` and bind only to `localhost` so that external traffic is always routed through Caddy: + +```bash +uvicorn app.main:app --host 127.0.0.1 --port 8000 --workers 2 +``` + +For a systemd service, create `/etc/systemd/system/portfoliohub.service`: + +```ini +[Unit] +Description=PortfolioHub API +After=network.target + +[Service] +User=www-data +WorkingDirectory=/opt/portfoliohub +EnvironmentFile=/opt/portfoliohub/.env +ExecStart=/opt/portfoliohub/.venv/bin/uvicorn app.main:app \ + --host 127.0.0.1 --port 8000 --workers 2 +Restart=on-failure + +[Install] +WantedBy=multi-user.target +``` + +Enable and start the service: + +```bash +sudo systemctl daemon-reload +sudo systemctl enable portfoliohub +sudo systemctl start portfoliohub +``` + +--- + +## 5. Docker Compose Setup (Optional) + +If you run PortfolioHub in Docker, you can add Caddy as a sidecar container. + +`docker-compose.yml`: + +```yaml +services: + app: + build: . + expose: + - "8000" + environment: + - PORTFOLIOHUB_ALLOWED_ORIGINS=https://your-domain.com + # add other env vars or reference an env_file + restart: unless-stopped + + caddy: + image: caddy:2-alpine + ports: + - "80:80" + - "443:443" + volumes: + - ./Caddyfile:/etc/caddy/Caddyfile:ro + - caddy_data:/data + - caddy_config:/config + depends_on: + - app + restart: unless-stopped + +volumes: + caddy_data: + caddy_config: +``` + +`Caddyfile` (placed next to `docker-compose.yml`): + +``` +your-domain.com { + reverse_proxy app:8000 +} +``` + +Note: inside a Docker Compose network, Caddy reaches the app container via its service name (`app`) instead of `localhost`. + +Start everything: + +```bash +docker compose up -d +``` + +--- + +## 6. Firewall Setup + +Open ports 80 (HTTP, needed for ACME certificate challenge) and 443 (HTTPS): + +```bash +# UFW (Ubuntu) +sudo ufw allow 80/tcp +sudo ufw allow 443/tcp +sudo ufw reload +``` + +```bash +# firewalld (RHEL/CentOS/Fedora) +sudo firewall-cmd --permanent --add-service=http +sudo firewall-cmd --permanent --add-service=https +sudo firewall-cmd --reload +``` + +--- + +## 7. Verify the Deployment + +```bash +# Health check over HTTPS +curl https://your-domain.com/health +# Expected: {"status":"ok"} +``` + +Check Caddy's status and certificate renewal: + +```bash +sudo systemctl status caddy +sudo journalctl -u caddy -f +```