Merge pull request #1 from genichin/copilot/add-deployment-docs-using-caddy

docs: add Caddy HTTPS deployment guide
This commit is contained in:
2026-04-06 14:20:33 +09:00
committed by GitHub
2 changed files with 214 additions and 0 deletions
+20
View File
@@ -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`.
+194
View File
@@ -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
```