mirror of
https://github.com/HeyPuter/puter.git
synced 2026-08-25 07:27:04 +00:00
Replaced Nginx with Caddy (#3378)
* Replace nginx with Caddy * updated caddy location * seprate logic for local and with domain caddy setup * updated install * updated install scripts new ps1 script with caddy, and removed instances of nginx * fix Caddy Host routing, restore TLS/SELinux/healthcheck, update docs --------- Co-authored-by: Daniel Salazar <daniel.salazar@puter.com>
This commit is contained in:
co-authored by
Daniel Salazar
parent
0a61b4e78b
commit
7cf3e03433
+2
-2
@@ -2,9 +2,9 @@
|
|||||||
# docker-compose.full.yml up -d`. None of the defaults below are safe for
|
# docker-compose.full.yml up -d`. None of the defaults below are safe for
|
||||||
# anything beyond a local laptop test.
|
# anything beyond a local laptop test.
|
||||||
|
|
||||||
# ── Public-facing ports (nginx) ---------------------------------------
|
# ── Public-facing ports (Caddy) ---------------------------------------
|
||||||
HTTP_PORT=80
|
HTTP_PORT=80
|
||||||
# HTTPS_PORT=443 # uncomment after you enable TLS in nginx/nginx.conf
|
# HTTPS_PORT=443 # uncomment after you enable TLS in caddy/Caddyfile
|
||||||
|
|
||||||
# ── MariaDB ------------------------------------------------------------
|
# ── MariaDB ------------------------------------------------------------
|
||||||
MARIADB_ROOT_PASSWORD=replace-with-strong-password
|
MARIADB_ROOT_PASSWORD=replace-with-strong-password
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# Reverse proxy in front of Puter — mirrors what the prod ALB does:
|
||||||
|
# accepts every Host header, forwards to the Puter container, and lets
|
||||||
|
# the Puter app handle subdomain-based routing internally (api.*,
|
||||||
|
# site.*, app.*, dev.*, plus the per-user subdomains under those).
|
||||||
|
#
|
||||||
|
# To enable TLS:
|
||||||
|
# 1. Drop a wildcard fullchain.pem + privkey.pem into ./puter/tls/
|
||||||
|
# (see "Step 3 — TLS" in doc/self-hosting.md).
|
||||||
|
# 2. Uncomment the `:443` block at the bottom of this file, and swap the
|
||||||
|
# `:80` block for the redirect shown alongside it.
|
||||||
|
# 3. Uncomment the `443:443` port mapping under the `caddy` service in
|
||||||
|
# docker-compose.yml, and HTTPS_PORT in .env.
|
||||||
|
# 4. Set `"protocol": "https"` and `"pub_port": 443` in config.json.
|
||||||
|
{
|
||||||
|
# Certs are supplied by the operator, not issued by Caddy. Puter serves
|
||||||
|
# per-user sites and apps on dynamic subdomains (<name>.site.<domain>,
|
||||||
|
# <name>.app.<domain>) — only a DNS-01 wildcard cert covers those, and
|
||||||
|
# DNS-01 needs a provider plugin that isn't in the stock caddy image.
|
||||||
|
# `off` stops Caddy attempting ACME on boot (it would fail and leave the
|
||||||
|
# site unreachable) and stops it inventing its own http→https redirects.
|
||||||
|
auto_https off
|
||||||
|
}
|
||||||
|
|
||||||
|
# Shared handling, imported by the HTTP and HTTPS site blocks below so the
|
||||||
|
# two can't drift apart.
|
||||||
|
(puter_routes) {
|
||||||
|
# Rough size cap that mirrors prod ALB defaults; tune for your uploads.
|
||||||
|
# Puter chunks large uploads, so 1 GiB per request is plenty.
|
||||||
|
request_body {
|
||||||
|
max_size 1024MiB
|
||||||
|
}
|
||||||
|
|
||||||
|
# RustFS — see the `s3` service in docker-compose.yml. Browsers PUT/GET
|
||||||
|
# here for presigned-URL uploads / downloads. Matched on the `s3.`
|
||||||
|
# subdomain of whatever the install domain is, so signature verification
|
||||||
|
# works (Caddy preserves the original Host end-to-end) and HTTPS stays
|
||||||
|
# clean — no mixed content from a port-9000 host publish.
|
||||||
|
@s3 header_regexp Host ^s3\.
|
||||||
|
handle @s3 {
|
||||||
|
reverse_proxy s3:9000
|
||||||
|
}
|
||||||
|
|
||||||
|
# Everything else, on every Host, goes to Puter — which routes on that
|
||||||
|
# Host internally. Caddy forwards it unchanged and adds X-Forwarded-For
|
||||||
|
# / -Proto / -Host, which is what `trust_proxy` in config.json counts.
|
||||||
|
handle {
|
||||||
|
reverse_proxy puter:4100 {
|
||||||
|
# Stream responses through unbuffered — Puter uses SSE and
|
||||||
|
# socket.io. WebSocket upgrades need no config of their own;
|
||||||
|
# Caddy proxies them by default.
|
||||||
|
flush_interval -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── HTTP (port 80) ─────────────────────────────────────────────────────
|
||||||
|
# A site address with no hostname is the catch-all: it answers for every
|
||||||
|
# Host, which is what Puter's subdomain routing requires.
|
||||||
|
:80 {
|
||||||
|
import puter_routes
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── HTTPS (port 443) — uncomment after dropping certs in ./puter/tls/ ──
|
||||||
|
# Replace the `:80` block above with a redirect to force HTTPS everywhere:
|
||||||
|
#
|
||||||
|
# :80 {
|
||||||
|
# redir https://{host}{uri} permanent
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# :443 {
|
||||||
|
# tls /etc/caddy/tls/fullchain.pem /etc/caddy/tls/privkey.pem
|
||||||
|
# import puter_routes
|
||||||
|
# }
|
||||||
+20
-16
@@ -1,6 +1,6 @@
|
|||||||
# Self-Hosting Puter
|
# Self-Hosting Puter
|
||||||
|
|
||||||
`docker-compose.yml` brings up Puter **plus every external service it needs** — MariaDB, Valkey, DynamoDB-local, RustFS S3, nginx — wired together. Closest thing to a production deployment you can self-manage on a single host.
|
`docker-compose.yml` brings up Puter **plus every external service it needs** — MariaDB, Valkey, DynamoDB-local, RustFS S3, Caddy — wired together. Closest thing to a production deployment you can self-manage on a single host.
|
||||||
|
|
||||||
## One-line installer (recommended)
|
## One-line installer (recommended)
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
curl -fsSL https://raw.githubusercontent.com/HeyPuter/puter/main/install.sh | sh
|
curl -fsSL https://raw.githubusercontent.com/HeyPuter/puter/main/install.sh | sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Generates secrets, writes `.env` + `puter/config/config.json`, downloads `docker-compose.yml` from the OSS repo, and runs `docker compose up -d`. Re-running is safe — it won't overwrite existing config (set `PUTER_FORCE=1` to rotate). Use the manual steps below if you want to inspect or tweak each step yourself.
|
Generates secrets, writes `.env` + `puter/config/config.json`, downloads `docker-compose.yml` + `caddy/Caddyfile` from the OSS repo, and runs `docker compose up -d`. Re-running is safe — it won't overwrite existing config (set `PUTER_FORCE=1` to rotate). Use the manual steps below if you want to inspect or tweak each step yourself.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ Generates secrets, writes `.env` + `puter/config/config.json`, downloads `docker
|
|||||||
|
|
||||||
| Container | Image | Role |
|
| Container | Image | Role |
|
||||||
| --------------- | ------------------------ | ---------------------------------------------------------- |
|
| --------------- | ------------------------ | ---------------------------------------------------------- |
|
||||||
| `puter-nginx` | `nginx:1.27-alpine` | Reverse proxy on 80 (and 443 if TLS); forwards to Puter |
|
| `puter-caddy` | `caddy:2.11-alpine` | Reverse proxy on 80 (and 443 if TLS); forwards to Puter |
|
||||||
| `puter` | `ghcr.io/heyputer/puter` | The app |
|
| `puter` | `ghcr.io/heyputer/puter` | The app |
|
||||||
| `puter-mariadb` | `mariadb:11` | SQL database — schema applied automatically on first boot |
|
| `puter-mariadb` | `mariadb:11` | SQL database — schema applied automatically on first boot |
|
||||||
| `puter-valkey` | `valkey/valkey:8-alpine` | Redis-compatible cache + rate-limiter |
|
| `puter-valkey` | `valkey/valkey:8-alpine` | Redis-compatible cache + rate-limiter |
|
||||||
@@ -65,7 +65,7 @@ S3_SECRET_KEY=$S3_SECRET_KEY
|
|||||||
S3_BUCKET=puter-local
|
S3_BUCKET=puter-local
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
mkdir -p puter/config puter/data puter/tls
|
mkdir -p puter/config puter/data puter/data/caddy puter/tls
|
||||||
cat > puter/config/config.json <<EOF
|
cat > puter/config/config.json <<EOF
|
||||||
{
|
{
|
||||||
"domain": "puter.localhost",
|
"domain": "puter.localhost",
|
||||||
@@ -141,14 +141,14 @@ Why these knobs:
|
|||||||
- `dynamo.aws` keys are dummies; DynamoDB-local doesn't validate them but the AWS SDK requires _something_. **Note:** DynamoDB uses `access_key` / `secret_key` (snake_case); S3 below uses `accessKeyId` / `secretAccessKey` (camelCase). Not interchangeable.
|
- `dynamo.aws` keys are dummies; DynamoDB-local doesn't validate them but the AWS SDK requires _something_. **Note:** DynamoDB uses `access_key` / `secret_key` (snake_case); S3 below uses `accessKeyId` / `secretAccessKey` (camelCase). Not interchangeable.
|
||||||
- `providers.ollama.enabled: false` — Puter auto-probes a local Ollama at `127.0.0.1:11434` by default; without one running you'd see `ECONNREFUSED` on every boot. To run a bundled Ollama, see [Optional: local LLM (Ollama)](#optional-local-llm-ollama) below.
|
- `providers.ollama.enabled: false` — Puter auto-probes a local Ollama at `127.0.0.1:11434` by default; without one running you'd see `ECONNREFUSED` on every boot. To run a bundled Ollama, see [Optional: local LLM (Ollama)](#optional-local-llm-ollama) below.
|
||||||
- `s3.s3Config.forcePathStyle: true` — RustFS / MinIO / fauxqs need path-style URLs (`<endpoint>/<bucket>`). Real AWS S3 wants virtual-hosted (`<bucket>.<endpoint>`) — drop this flag (or set `false`) when you swap to real S3.
|
- `s3.s3Config.forcePathStyle: true` — RustFS / MinIO / fauxqs need path-style URLs (`<endpoint>/<bucket>`). Real AWS S3 wants virtual-hosted (`<bucket>.<endpoint>`) — drop this flag (or set `false`) when you swap to real S3.
|
||||||
- `s3.s3Config.publicEndpoint` — `endpoint` (`http://s3:9000`) only resolves inside the docker network; presigned upload/download URLs handed to the browser need a host-reachable URL. nginx routes the `s3.<domain>` subdomain to RustFS internally and preserves the Host header end-to-end (required for S3 signature validation), so the browser hits the same port/protocol as the rest of the app — no separate published port, no mixed-content surprises when you turn on TLS. Switch to `https://s3.<your-domain>` once you enable TLS in Step 3. Real AWS S3 doesn't need this — its endpoint is already public; drop the field entirely.
|
- `s3.s3Config.publicEndpoint` — `endpoint` (`http://s3:9000`) only resolves inside the docker network; presigned upload/download URLs handed to the browser need a host-reachable URL. Caddy routes the `s3.<domain>` subdomain to RustFS internally and preserves the Host header end-to-end (required for S3 signature validation), so the browser hits the same port/protocol as the rest of the app — no separate published port, no mixed-content surprises when you turn on TLS. Switch to `https://s3.<your-domain>` once you enable TLS in Step 3. Real AWS S3 doesn't need this — its endpoint is already public; drop the field entirely.
|
||||||
- `trust_proxy: 1` — nginx terminates TLS and forwards `X-Forwarded-For`. Without this, `req.ip` is the docker-network address of the nginx container instead of the real client IP, which breaks rate limiting and IP-based audit logs. `1` = one trusted hop (nginx). Bump to `2` if you put Cloudflare in front of nginx; never set `true` (it trusts every hop and makes XFF forgeable).
|
- `trust_proxy: 1` — Caddy terminates TLS and forwards `X-Forwarded-For`. Without this, `req.ip` is the docker-network address of the Caddy container instead of the real client IP, which breaks rate limiting and IP-based audit logs. `1` = one trusted hop (Caddy). Bump to `2` if you put Cloudflare in front of Caddy; never set `true` (it trusts every hop and makes XFF forgeable).
|
||||||
|
|
||||||
> If you ever change `MARIADB_PASSWORD` after first boot, `.env` alone won't update MariaDB — its credentials are baked into `./puter/data/mariadb/` on first init. Either rotate the password inside MariaDB by hand or `docker compose down && rm -rf ./puter/data/mariadb` to start fresh.
|
> If you ever change `MARIADB_PASSWORD` after first boot, `.env` alone won't update MariaDB — its credentials are baked into `./puter/data/mariadb/` on first init. Either rotate the password inside MariaDB by hand or `docker compose down && rm -rf ./puter/data/mariadb` to start fresh.
|
||||||
|
|
||||||
## Step 2 — Point DNS at the server \[Optional\]
|
## Step 2 — Point DNS at the server \[Optional\]
|
||||||
|
|
||||||
In your DNS provider, add records for the main domain plus the subdomains Puter and nginx route on (`api.*`, `site.*`, `app.*`, `s3.*`):
|
In your DNS provider, add records for the main domain plus the subdomains Puter and Caddy route on (`api.*`, `site.*`, `app.*`, `s3.*`):
|
||||||
|
|
||||||
```
|
```
|
||||||
A puter.localhost → <your server's public IP>
|
A puter.localhost → <your server's public IP>
|
||||||
@@ -163,7 +163,7 @@ A dev.puter.localhost → <your server's public IP>
|
|||||||
A *.dev.puter.localhost → <your server's public IP>
|
A *.dev.puter.localhost → <your server's public IP>
|
||||||
```
|
```
|
||||||
|
|
||||||
The wildcards are required — Puter routes via subdomains (`api.*`, `app.*`, etc.) and nginx routes browser S3 traffic via `s3.*` to RustFS.
|
The wildcards are required — Puter routes via subdomains (`api.*`, `app.*`, etc.) and Caddy routes browser S3 traffic via `s3.*` to RustFS.
|
||||||
|
|
||||||
## Step 3 — TLS (recommended for public installs) \[Optional\]
|
## Step 3 — TLS (recommended for public installs) \[Optional\]
|
||||||
|
|
||||||
@@ -182,13 +182,15 @@ sudo certbot certonly --manual --preferred-challenges dns \
|
|||||||
|
|
||||||
The cert needs to cover `*.puter.localhost` so that `s3.puter.localhost` (browser S3 endpoint), plus Puter's own `api.*` / `app.*` subdomains, all validate.
|
The cert needs to cover `*.puter.localhost` so that `s3.puter.localhost` (browser S3 endpoint), plus Puter's own `api.*` / `app.*` subdomains, all validate.
|
||||||
|
|
||||||
|
> **Why not Caddy's automatic HTTPS?** Caddy issues certs over ACME by itself, but only for hostnames it knows up front, and only over HTTP-01 with the stock image. Puter serves every user's site and app on a subdomain it invents at runtime (`<name>.site.<domain>`, `<name>.app.<domain>`), which needs a **wildcard** cert — and wildcards require DNS-01, which requires a DNS-provider plugin that isn't in `caddy:2.11-alpine`. So `caddy/Caddyfile` sets `auto_https off` and reads the cert you supply below. If you'd rather have Caddy manage certs, build an image with the plugin for your DNS provider (`xcaddy build --with github.com/caddy-dns/<provider>`) and swap the `tls` line for a `tls { dns <provider> … }` block.
|
||||||
|
|
||||||
Drop the resulting `fullchain.pem` and `privkey.pem` into `./puter/tls/`.
|
Drop the resulting `fullchain.pem` and `privkey.pem` into `./puter/tls/`.
|
||||||
|
|
||||||
**Wire nginx to use them:**
|
**Wire Caddy to use them:**
|
||||||
|
|
||||||
1. Open [nginx/nginx.conf](../nginx/nginx.conf), uncomment **both** `# server { listen 443 ssl … }` blocks (one for `s3.*`, one for the catch-all).
|
1. Open [caddy/Caddyfile](../caddy/Caddyfile) and uncomment the `# :443 { … }` block at the bottom.
|
||||||
2. (Optional) Replace the body of the port-80 blocks with `return 301 https://$host$request_uri;` to force HTTPS everywhere.
|
2. (Optional but recommended) Replace the plain `:80 { import puter_routes }` block with the `redir` version shown alongside it, to force HTTPS everywhere.
|
||||||
3. In [docker-compose.yml](../docker-compose.yml), uncomment the `443:443` port mapping under the `nginx` service.
|
3. In [docker-compose.yml](../docker-compose.yml), uncomment the `443:443` port mapping under the `caddy` service.
|
||||||
4. In `.env`, uncomment `HTTPS_PORT=443`.
|
4. In `.env`, uncomment `HTTPS_PORT=443`.
|
||||||
5. In `config.json`, switch:
|
5. In `config.json`, switch:
|
||||||
```json
|
```json
|
||||||
@@ -201,9 +203,9 @@ Drop the resulting `fullchain.pem` and `privkey.pem` into `./puter/tls/`.
|
|||||||
|
|
||||||
## Running behind your own reverse proxy
|
## Running behind your own reverse proxy
|
||||||
|
|
||||||
The bundled `puter-nginx` mirrors production and is the supported default — it terminates TLS (Step 3) and forwards every Host to Puter. But plenty of self-hosters already run their own edge proxy (Caddy, Traefik, HAProxy, a cloud load balancer, another nginx). You can put Puter behind it instead; Puter doesn't terminate TLS itself in any setup, so "your proxy does TLS" is just a matter of pointing it at the Puter container and getting a handful of details right.
|
The bundled `puter-caddy` mirrors production and is the supported default — it terminates TLS (Step 3) and forwards every Host to Puter. But plenty of self-hosters already run their own edge proxy (Traefik, nginx, HAProxy, a cloud load balancer, another Caddy). You can put Puter behind it instead; Puter doesn't terminate TLS itself in any setup, so "your proxy does TLS" is just a matter of pointing it at the Puter container and getting a handful of details right.
|
||||||
|
|
||||||
You can keep the bundled nginx as the single hop your proxy talks to, or bypass it and forward straight to the Puter container on port `4100` (uncomment the `4100:4100` mapping under the `puter` service in [docker-compose.yml](../docker-compose.yml), or attach your proxy to the compose network). Either works — what matters is the rules below.
|
You can keep the bundled Caddy as the single hop your proxy talks to, or bypass it and forward straight to the Puter container on port `4100` (uncomment the `4100:4100` mapping under the `puter` service in [docker-compose.yml](../docker-compose.yml), or attach your proxy to the compose network). Either works — what matters is the rules below.
|
||||||
|
|
||||||
**The rules that actually matter** (getting any of these wrong is what causes the redirect loops and "Invalid Host header" failures people hit):
|
**The rules that actually matter** (getting any of these wrong is what causes the redirect loops and "Invalid Host header" failures people hit):
|
||||||
|
|
||||||
@@ -219,7 +221,7 @@ You can keep the bundled nginx as the single hop your proxy talks to, or bypass
|
|||||||
- `X-Forwarded-For` — the real client IP (rate limiting + audit logs).
|
- `X-Forwarded-For` — the real client IP (rate limiting + audit logs).
|
||||||
- `Upgrade` / `Connection` — passed through for WebSocket / socket.io upgrades, or the realtime connection silently fails.
|
- `Upgrade` / `Connection` — passed through for WebSocket / socket.io upgrades, or the realtime connection silently fails.
|
||||||
|
|
||||||
5. **Set `trust_proxy` to the number of hops in front of Puter.** One external proxy talking directly to Puter → `"trust_proxy": 1` (installer: `PUTER_TRUST_PROXY=1`). A second proxy in front (e.g. Cloudflare → your proxy → Puter) → `2`. This is the count of proxies between the client and Puter, **including** the bundled nginx if you keep it. Too low and `req.ip` becomes a proxy address (breaks rate limiting); never set `true` (it trusts every hop and makes `X-Forwarded-For` forgeable).
|
5. **Set `trust_proxy` to the number of hops in front of Puter.** One external proxy talking directly to Puter → `"trust_proxy": 1` (installer: `PUTER_TRUST_PROXY=1`). A second proxy in front (e.g. Cloudflare → your proxy → Puter) → `2`. This is the count of proxies between the client and Puter, **including** the bundled Caddy if you keep it. Too low and `req.ip` becomes a proxy address (breaks rate limiting); never set `true` (it trusts every hop and makes `X-Forwarded-For` forgeable).
|
||||||
|
|
||||||
6. **Route the wildcard to Puter.** Your proxy must forward `*.<domain>` (covering `api.*`, `app.*`, and `s3.<domain>`) and `*.site.<domain>` (and any other hosting domains) to Puter — same wildcard DNS as Step 2. Puter does the per-subdomain routing internally; the proxy just needs to hand it the traffic with the Host intact.
|
6. **Route the wildcard to Puter.** Your proxy must forward `*.<domain>` (covering `api.*`, `app.*`, and `s3.<domain>`) and `*.site.<domain>` (and any other hosting domains) to Puter — same wildcard DNS as Step 2. Puter does the per-subdomain routing internally; the proxy just needs to hand it the traffic with the Host intact.
|
||||||
|
|
||||||
@@ -227,6 +229,8 @@ With those in place the rest of the stack is unchanged — `docker compose up -d
|
|||||||
|
|
||||||
## Step 4 — Bring it up
|
## Step 4 — Bring it up
|
||||||
|
|
||||||
|
Both [docker-compose.yml](../docker-compose.yml) and [caddy/Caddyfile](../caddy/Caddyfile) need to sit next to your `.env` — clone the repo, or copy those two paths out of it. The Caddyfile is bind-mounted read-only; if it's missing, Docker creates a *directory* at that path and `puter-caddy` dies with "not a directory".
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
```
|
```
|
||||||
@@ -469,7 +473,7 @@ Migrations re-apply idempotently across pulls. Volumes are preserved.
|
|||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
**Site loads but I get "Bad Gateway" / nginx errors.**
|
**Site loads but I get a 502 / "Bad Gateway" from Caddy.**
|
||||||
The puter container failed to come up. `docker compose logs puter` will tell you which dependency rejected it (most often DB password mismatch between `.env` and `config.json`).
|
The puter container failed to come up. `docker compose logs puter` will tell you which dependency rejected it (most often DB password mismatch between `.env` and `config.json`).
|
||||||
|
|
||||||
**Login screen says "admin password not set".**
|
**Login screen says "admin password not set".**
|
||||||
|
|||||||
+22
-12
@@ -2,7 +2,7 @@
|
|||||||
# Self-hosted Puter — full stack.
|
# Self-hosted Puter — full stack.
|
||||||
#
|
#
|
||||||
# Brings up Puter + every external service it needs:
|
# Brings up Puter + every external service it needs:
|
||||||
# - nginx : reverse proxy (mirrors prod ALB; handles TLS + Host fan-out)
|
# - caddy : reverse proxy (mirrors prod ALB; handles TLS + Host fan-out)
|
||||||
# - valkey : redis-compatible cache / rate-limiter backend
|
# - valkey : redis-compatible cache / rate-limiter backend
|
||||||
# - mariadb : SQL database (Puter applies its schema on first boot)
|
# - mariadb : SQL database (Puter applies its schema on first boot)
|
||||||
# - dynamo : DynamoDB-local (KV store; Puter creates the table itself)
|
# - dynamo : DynamoDB-local (KV store; Puter creates the table itself)
|
||||||
@@ -23,7 +23,8 @@
|
|||||||
#
|
#
|
||||||
# Production:
|
# Production:
|
||||||
# - Always replace the default passwords / S3 keys / Puter secrets.
|
# - Always replace the default passwords / S3 keys / Puter secrets.
|
||||||
# - Front Puter with TLS-terminating reverse proxy (Caddy / nginx).
|
# - Enable TLS on the bundled Caddy (see caddy/Caddyfile) or front the
|
||||||
|
# stack with your own TLS-terminating proxy.
|
||||||
# - Move state-bearing volumes to a backed-up location.
|
# - Move state-bearing volumes to a backed-up location.
|
||||||
|
|
||||||
services:
|
services:
|
||||||
@@ -118,7 +119,7 @@ services:
|
|||||||
RUSTFS_SECRET_KEY: ${S3_SECRET_KEY:-puter-secret-change-me}
|
RUSTFS_SECRET_KEY: ${S3_SECRET_KEY:-puter-secret-change-me}
|
||||||
volumes:
|
volumes:
|
||||||
- ./puter/data/s3:/data:z
|
- ./puter/data/s3:/data:z
|
||||||
# Internal-only — browsers reach RustFS via nginx (`s3.<domain>`),
|
# Internal-only — browsers reach RustFS via Caddy (`s3.<domain>`),
|
||||||
# which preserves the Host header for S3 signature validation and
|
# which preserves the Host header for S3 signature validation and
|
||||||
# rides the same TLS termination as Puter. Uncomment to also expose
|
# rides the same TLS termination as Puter. Uncomment to also expose
|
||||||
# 9000 directly on the host for `aws-cli` / debugging.
|
# 9000 directly on the host for `aws-cli` / debugging.
|
||||||
@@ -249,7 +250,7 @@ services:
|
|||||||
condition: service_started
|
condition: service_started
|
||||||
s3-init:
|
s3-init:
|
||||||
condition: service_completed_successfully
|
condition: service_completed_successfully
|
||||||
# Internal-only: nginx reaches it on the compose network. Uncomment
|
# Internal-only: Caddy reaches it on the compose network. Uncomment
|
||||||
# to also expose port 4100 directly on the host (useful for debugging).
|
# to also expose port 4100 directly on the host (useful for debugging).
|
||||||
# ports:
|
# ports:
|
||||||
# - "4100:4100"
|
# - "4100:4100"
|
||||||
@@ -270,23 +271,32 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
start_period: 30s
|
start_period: 30s
|
||||||
|
|
||||||
nginx:
|
caddy:
|
||||||
image: nginx:1.27-alpine
|
image: caddy:2.11-alpine
|
||||||
container_name: puter-nginx
|
container_name: puter-caddy
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
depends_on:
|
||||||
puter:
|
puter:
|
||||||
condition: service_started
|
condition: service_started
|
||||||
ports:
|
ports:
|
||||||
- "${HTTP_PORT:-80}:80"
|
- "${HTTP_PORT:-80}:80"
|
||||||
# Uncomment when you enable TLS in nginx/nginx.conf:
|
# Uncomment when you enable TLS in caddy/Caddyfile:
|
||||||
# - "${HTTPS_PORT:-443}:443"
|
# - "${HTTPS_PORT:-443}:443"
|
||||||
volumes:
|
volumes:
|
||||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro,z
|
- ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro,z
|
||||||
# TLS certs (fullchain.pem + privkey.pem). Read-only inside.
|
# TLS certs (wildcard fullchain.pem + privkey.pem). Read-only inside.
|
||||||
- ./puter/tls:/etc/nginx/tls:ro,z
|
- ./puter/tls:/etc/caddy/tls:ro,z
|
||||||
|
# Caddy's own state — the cert store it would use if you swap this
|
||||||
|
# config over to ACME. Kept under ./puter/data with everything else
|
||||||
|
# so one directory is the whole backup.
|
||||||
|
- ./puter/data/caddy:/data:z
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "wget -qO- --tries=1 --timeout=2 http://localhost/ || exit 1"]
|
# Hits Caddy's local-only admin API rather than proxying through to
|
||||||
|
# Puter: this reports whether the proxy itself is up and configured,
|
||||||
|
# and keeps working once the `:80` block becomes an HTTPS redirect.
|
||||||
|
# 127.0.0.1, not localhost — the admin endpoint is IPv4-only and
|
||||||
|
# busybox wget tries ::1 first.
|
||||||
|
test: ["CMD-SHELL", "wget -qO- --tries=1 --timeout=2 http://127.0.0.1:2019/config/ >/dev/null || exit 1"]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 3s
|
timeout: 3s
|
||||||
retries: 5
|
retries: 5
|
||||||
|
|||||||
+15
-12
@@ -11,7 +11,7 @@
|
|||||||
# What this does, in order:
|
# What this does, in order:
|
||||||
# 1. Checks that docker (with the compose plugin) exists.
|
# 1. Checks that docker (with the compose plugin) exists.
|
||||||
# 2. Creates ./puter-selfhosted/ (override with $env:PUTER_DIR).
|
# 2. Creates ./puter-selfhosted/ (override with $env:PUTER_DIR).
|
||||||
# 3. Downloads docker-compose.yml + nginx.conf from the OSS repo.
|
# 3. Downloads docker-compose.yml + caddy/Caddyfile from the OSS repo.
|
||||||
# 4. Generates fresh secrets and writes .env + puter/config/config.json.
|
# 4. Generates fresh secrets and writes .env + puter/config/config.json.
|
||||||
# 5. Runs `docker compose up -d` and prints how to find the admin password.
|
# 5. Runs `docker compose up -d` and prints how to find the admin password.
|
||||||
#
|
#
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
# PUTER_DIR install directory (default: ./puter-selfhosted)
|
# PUTER_DIR install directory (default: ./puter-selfhosted)
|
||||||
# PUTER_URL base URL to fetch docker-compose.yml (default: GitHub raw, main branch)
|
# PUTER_URL base URL to fetch docker-compose.yml (default: GitHub raw, main branch)
|
||||||
# PUTER_DOMAIN domain Puter will serve on (default: puter.localhost)
|
# PUTER_DOMAIN domain Puter will serve on (default: puter.localhost)
|
||||||
# PUTER_PORT HTTP port for nginx (default: 80)
|
# PUTER_PORT HTTP port for Caddy (default: 80)
|
||||||
# PUTER_FORCE set to 1 to overwrite existing .env / config.json
|
# PUTER_FORCE set to 1 to overwrite existing .env / config.json
|
||||||
|
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
@@ -77,10 +77,10 @@ if ($LASTEXITCODE -ne 0) {
|
|||||||
# ── Step 2: install dir ─────────────────────────────────────────────
|
# ── Step 2: install dir ─────────────────────────────────────────────
|
||||||
$null = New-Item -ItemType Directory -Force -Path $PuterDir
|
$null = New-Item -ItemType Directory -Force -Path $PuterDir
|
||||||
Set-Location $PuterDir
|
Set-Location $PuterDir
|
||||||
$null = New-Item -ItemType Directory -Force -Path 'puter/config', 'puter/data', 'puter/tls'
|
$null = New-Item -ItemType Directory -Force -Path 'puter/config', 'puter/data', 'puter/data/caddy', 'puter/tls'
|
||||||
Write-Log "install dir: $((Get-Location).Path)"
|
Write-Log "install dir: $((Get-Location).Path)"
|
||||||
|
|
||||||
# ── Step 3: docker-compose.yml + nginx config ──────────────────────
|
# ── Step 3: docker-compose.yml + Caddy config ──────────────────────
|
||||||
Write-Log "downloading docker-compose.yml from $PuterUrl"
|
Write-Log "downloading docker-compose.yml from $PuterUrl"
|
||||||
try {
|
try {
|
||||||
Invoke-WebRequest -Uri "$PuterUrl/docker-compose.yml" -OutFile 'docker-compose.yml' -UseBasicParsing
|
Invoke-WebRequest -Uri "$PuterUrl/docker-compose.yml" -OutFile 'docker-compose.yml' -UseBasicParsing
|
||||||
@@ -88,17 +88,19 @@ try {
|
|||||||
Die "could not fetch $PuterUrl/docker-compose.yml — $_"
|
Die "could not fetch $PuterUrl/docker-compose.yml — $_"
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Log "downloading nginx/nginx.conf from $PuterUrl"
|
# The Caddyfile is domain-agnostic — it answers on every Host and leaves
|
||||||
$null = New-Item -ItemType Directory -Force -Path 'nginx'
|
# the subdomain routing to Puter — so there's nothing to template in.
|
||||||
|
Write-Log "downloading caddy/Caddyfile from $PuterUrl"
|
||||||
|
$null = New-Item -ItemType Directory -Force -Path 'caddy'
|
||||||
# If the path was previously auto-created as a directory by a failed
|
# If the path was previously auto-created as a directory by a failed
|
||||||
# `compose up`, remove it so we can write the file there.
|
# `compose up`, remove it so we can write the file there.
|
||||||
if (Test-Path 'nginx/nginx.conf' -PathType Container) {
|
if (Test-Path 'caddy/Caddyfile' -PathType Container) {
|
||||||
Remove-Item 'nginx/nginx.conf' -Recurse -Force
|
Remove-Item 'caddy/Caddyfile' -Recurse -Force
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Invoke-WebRequest -Uri "$PuterUrl/nginx/nginx.conf" -OutFile 'nginx/nginx.conf' -UseBasicParsing
|
Invoke-WebRequest -Uri "$PuterUrl/caddy/Caddyfile" -OutFile 'caddy/Caddyfile' -UseBasicParsing
|
||||||
} catch {
|
} catch {
|
||||||
Die "could not fetch $PuterUrl/nginx/nginx.conf — $_"
|
Die "could not fetch $PuterUrl/caddy/Caddyfile — $_"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Step 4: secrets, .env, config.json ──────────────────────────────
|
# ── Step 4: secrets, .env, config.json ──────────────────────────────
|
||||||
@@ -121,7 +123,8 @@ if ($writeConfig) {
|
|||||||
|
|
||||||
$envContent = @"
|
$envContent = @"
|
||||||
HTTP_PORT=$PuterPort
|
HTTP_PORT=$PuterPort
|
||||||
# HTTPS_PORT=443 # uncomment after enabling TLS (see doc/selfhosting/full-stack.md)
|
# HTTPS_PORT=443 # uncomment after enabling TLS in caddy/Caddyfile
|
||||||
|
# # (see "Step 3 — TLS" in doc/self-hosting.md)
|
||||||
|
|
||||||
MARIADB_ROOT_PASSWORD=$mariadbRootPw
|
MARIADB_ROOT_PASSWORD=$mariadbRootPw
|
||||||
MARIADB_DATABASE=puter
|
MARIADB_DATABASE=puter
|
||||||
@@ -205,4 +208,4 @@ Write-Log " cd $PuterDir; docker compose logs -f puter"
|
|||||||
Write-Log ''
|
Write-Log ''
|
||||||
Write-Log "open http://${PuterDomain}:${PuterPort} once the puter container is healthy."
|
Write-Log "open http://${PuterDomain}:${PuterPort} once the puter container is healthy."
|
||||||
Write-Log 'first-boot admin password is logged once — grab it with:'
|
Write-Log 'first-boot admin password is logged once — grab it with:'
|
||||||
Write-Log " cd $PuterDir; docker compose logs puter | Select-String password"
|
Write-Log " cd $PuterDir; docker compose logs puter | Select-String password"
|
||||||
|
|||||||
+36
-18
@@ -7,7 +7,8 @@
|
|||||||
# What this does, in order:
|
# What this does, in order:
|
||||||
# 1. Checks that docker (with the compose plugin), curl, and openssl exist.
|
# 1. Checks that docker (with the compose plugin), curl, and openssl exist.
|
||||||
# 2. Creates ./puter-selfhosted/ (override with PUTER_DIR=...).
|
# 2. Creates ./puter-selfhosted/ (override with PUTER_DIR=...).
|
||||||
# 3. Downloads docker-compose.yml from the OSS repo (raw.githubusercontent.com).
|
# 3. Downloads docker-compose.yml + caddy/Caddyfile from the OSS repo
|
||||||
|
# (raw.githubusercontent.com).
|
||||||
# 4. Generates fresh secrets and writes .env + puter/config/config.json.
|
# 4. Generates fresh secrets and writes .env + puter/config/config.json.
|
||||||
# 5. Runs `docker compose up -d` and prints the first-boot admin password.
|
# 5. Runs `docker compose up -d` and prints the first-boot admin password.
|
||||||
#
|
#
|
||||||
@@ -19,15 +20,17 @@
|
|||||||
# PUTER_DIR install directory (default: ./puter-selfhosted)
|
# PUTER_DIR install directory (default: ./puter-selfhosted)
|
||||||
# PUTER_URL base URL to fetch docker-compose.yml (default: GitHub raw, main branch)
|
# PUTER_URL base URL to fetch docker-compose.yml (default: GitHub raw, main branch)
|
||||||
# PUTER_DOMAIN domain Puter will serve on (default: puter.localhost)
|
# PUTER_DOMAIN domain Puter will serve on (default: puter.localhost)
|
||||||
# PUTER_PORT HTTP port for nginx (default: 80)
|
# PUTER_PORT HTTP port for Caddy (default: 80)
|
||||||
# PUTER_PROTOCOL public scheme: http | https (default: http)
|
# PUTER_PROTOCOL public scheme: http | https (default: http)
|
||||||
# Set https when a TLS-terminating reverse proxy (Caddy,
|
# Set https once TLS is terminating in front of Puter —
|
||||||
# Traefik, nginx, a cloud LB) sits in front — Puter then
|
# either the bundled Caddy with your certs (see "Step 3 —
|
||||||
# builds https:// origins, S3 URLs, and redirects. See
|
# TLS" in doc/self-hosting.md) or your own proxy (Traefik,
|
||||||
# "Running behind your own reverse proxy" in doc/self-hosting.md.
|
# a cloud LB). Puter then builds https:// origins, S3
|
||||||
|
# URLs, and redirects. Leave it http and the installer
|
||||||
|
# serves plain HTTP on PUTER_PORT.
|
||||||
# PUTER_TRUST_PROXY number of reverse-proxy hops in front (default: 1)
|
# PUTER_TRUST_PROXY number of reverse-proxy hops in front (default: 1)
|
||||||
# 1 = the bundled nginx (or a single external proxy);
|
# 1 = the bundled Caddy (or a single external proxy);
|
||||||
# 2 = two hops (e.g. Cloudflare → your proxy → Puter).
|
# 2 = two hops (e.g. Cloudflare → Caddy → Puter).
|
||||||
# PUTER_ENV prod | dev (default: prod)
|
# PUTER_ENV prod | dev (default: prod)
|
||||||
# PUTER_FORCE set to 1 to overwrite existing .env / config.json
|
# PUTER_FORCE set to 1 to overwrite existing .env / config.json
|
||||||
|
|
||||||
@@ -72,25 +75,27 @@ mkdir -p puter/config puter/data puter/tls
|
|||||||
# bind-mount roots sidesteps the mismatch without guessing each image's
|
# bind-mount roots sidesteps the mismatch without guessing each image's
|
||||||
# internal UID. (Docker Desktop on macOS/Windows papers over this with
|
# internal UID. (Docker Desktop on macOS/Windows papers over this with
|
||||||
# its VM layer; native Linux docker on Debian/Alpine doesn't.)
|
# its VM layer; native Linux docker on Debian/Alpine doesn't.)
|
||||||
mkdir -p puter/data/valkey puter/data/mariadb puter/data/dynamo puter/data/s3 puter/data/puter
|
mkdir -p puter/data/valkey puter/data/mariadb puter/data/dynamo puter/data/s3 puter/data/puter puter/data/caddy
|
||||||
chmod 0777 puter/data/valkey puter/data/mariadb puter/data/dynamo puter/data/s3 puter/data/puter
|
chmod 0777 puter/data/valkey puter/data/mariadb puter/data/dynamo puter/data/s3 puter/data/puter puter/data/caddy
|
||||||
log "install dir: $(pwd)"
|
log "install dir: $(pwd)"
|
||||||
|
|
||||||
# ── Step 3: docker-compose.yml + nginx config ──────────────────────
|
# ── Step 3: docker-compose.yml + Caddy config ──────────────────────
|
||||||
log "downloading docker-compose.yml from $PUTER_URL"
|
log "downloading docker-compose.yml from $PUTER_URL"
|
||||||
curl -fsSL "$PUTER_URL/docker-compose.yml" -o docker-compose.yml \
|
curl -fsSL "$PUTER_URL/docker-compose.yml" -o docker-compose.yml \
|
||||||
|| die "could not fetch $PUTER_URL/docker-compose.yml"
|
|| die "could not fetch $PUTER_URL/docker-compose.yml"
|
||||||
|
|
||||||
# nginx is mounted as `./nginx/nginx.conf:/etc/nginx/nginx.conf:ro` — if
|
# Caddy is mounted as `./caddy/Caddyfile:/etc/caddy/Caddyfile:ro,z` — if
|
||||||
# the host file is missing, docker silently creates a directory at that
|
# the host file is missing, docker silently creates a directory at that
|
||||||
# path and the mount fails with "not a directory" at container start.
|
# path and the mount fails with "not a directory" at container start.
|
||||||
log "downloading nginx/nginx.conf from $PUTER_URL"
|
# The Caddyfile is domain-agnostic — it answers on every Host and leaves
|
||||||
mkdir -p nginx
|
# the subdomain routing to Puter — so there's nothing to template in.
|
||||||
|
log "downloading caddy/Caddyfile from $PUTER_URL"
|
||||||
|
mkdir -p caddy
|
||||||
# If the path was previously auto-created as a dir by a failed `compose up`,
|
# If the path was previously auto-created as a dir by a failed `compose up`,
|
||||||
# remove it so curl can write the file.
|
# remove it so curl can write the file.
|
||||||
[ -d nginx/nginx.conf ] && rmdir nginx/nginx.conf 2>/dev/null || true
|
[ -d caddy/Caddyfile ] && rmdir caddy/Caddyfile 2>/dev/null || true
|
||||||
curl -fsSL "$PUTER_URL/nginx/nginx.conf" -o nginx/nginx.conf \
|
curl -fsSL "$PUTER_URL/caddy/Caddyfile" -o caddy/Caddyfile \
|
||||||
|| die "could not fetch $PUTER_URL/nginx/nginx.conf"
|
|| die "could not fetch $PUTER_URL/caddy/Caddyfile"
|
||||||
|
|
||||||
# ── Step 4: secrets, .env, config.json ──────────────────────────────
|
# ── Step 4: secrets, .env, config.json ──────────────────────────────
|
||||||
write_config=1
|
write_config=1
|
||||||
@@ -113,7 +118,8 @@ if [ "$write_config" = "1" ]; then
|
|||||||
|
|
||||||
cat > .env <<EOF
|
cat > .env <<EOF
|
||||||
HTTP_PORT=$PUTER_PORT
|
HTTP_PORT=$PUTER_PORT
|
||||||
# HTTPS_PORT=443 # uncomment after enabling TLS (see doc/selfhosting/full-stack.md)
|
# HTTPS_PORT=443 # uncomment after enabling TLS in caddy/Caddyfile
|
||||||
|
# # (see "Step 3 — TLS" in doc/self-hosting.md)
|
||||||
|
|
||||||
MARIADB_ROOT_PASSWORD=$MARIADB_ROOT_PASSWORD
|
MARIADB_ROOT_PASSWORD=$MARIADB_ROOT_PASSWORD
|
||||||
MARIADB_DATABASE=puter
|
MARIADB_DATABASE=puter
|
||||||
@@ -190,6 +196,18 @@ EOF
|
|||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# `https` only works if something in front is actually terminating it. The
|
||||||
|
# bundled Caddy does that from ./puter/tls once you enable its `:443` block
|
||||||
|
# (see "Step 3 — TLS" in doc/self-hosting.md); your own edge proxy is fine
|
||||||
|
# too. Neither in place means Puter hands out https:// URLs for an endpoint
|
||||||
|
# that only speaks HTTP — broken logins and mixed content.
|
||||||
|
if [ "$PUTER_PROTOCOL" = "https" ] && [ ! -f puter/tls/fullchain.pem ]; then
|
||||||
|
warn "PUTER_PROTOCOL=https but ./puter/tls/fullchain.pem is missing."
|
||||||
|
warn "The bundled Caddy is still serving plain HTTP on port $PUTER_PORT."
|
||||||
|
warn "Either drop a wildcard cert into ./puter/tls and uncomment the :443"
|
||||||
|
warn "block in caddy/Caddyfile, or terminate TLS in your own proxy."
|
||||||
|
fi
|
||||||
|
|
||||||
# ── Step 5: bring it up ─────────────────────────────────────────────
|
# ── Step 5: bring it up ─────────────────────────────────────────────
|
||||||
log "docker compose up -d"
|
log "docker compose up -d"
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
|
|||||||
@@ -1,135 +0,0 @@
|
|||||||
# Reverse proxy in front of Puter — mirrors what the prod ALB does:
|
|
||||||
# accepts every Host header, forwards to the Puter container, and lets
|
|
||||||
# the Puter app handle subdomain-based routing internally (api.*,
|
|
||||||
# site.*, app.*, etc).
|
|
||||||
#
|
|
||||||
# To enable TLS:
|
|
||||||
# 1. Drop your fullchain.pem + privkey.pem into ./puter/tls/.
|
|
||||||
# 2. Uncomment the 443 server{} block below.
|
|
||||||
# 3. Update server_name to your domain (and wildcard subdomains).
|
|
||||||
|
|
||||||
worker_processes auto;
|
|
||||||
events {
|
|
||||||
worker_connections 4096;
|
|
||||||
}
|
|
||||||
|
|
||||||
http {
|
|
||||||
# Required for Puter's WebSocket / socket.io upgrades.
|
|
||||||
map $http_upgrade $connection_upgrade {
|
|
||||||
default upgrade;
|
|
||||||
'' close;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Rough size cap that mirrors prod ALB defaults; tune for your
|
|
||||||
# uploads. Puter chunks large uploads, so 1 GiB per request is plenty.
|
|
||||||
client_max_body_size 1024m;
|
|
||||||
proxy_read_timeout 600s;
|
|
||||||
proxy_send_timeout 600s;
|
|
||||||
proxy_buffering off;
|
|
||||||
server_tokens off;
|
|
||||||
|
|
||||||
upstream puter_backend {
|
|
||||||
server puter:4100;
|
|
||||||
keepalive 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
upstream s3_backend {
|
|
||||||
# RustFS — see `s3` service in docker-compose.full.yml. Browsers
|
|
||||||
# PUT/GET here for presigned-URL uploads / downloads. Routed via
|
|
||||||
# the `s3.<domain>` subdomain so signature verification works
|
|
||||||
# (Host header preserved end-to-end) and so HTTPS stays clean
|
|
||||||
# (no mixed-content from a port-9000 host publish).
|
|
||||||
server s3:9000;
|
|
||||||
keepalive 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── HTTP (port 80) ─────────────────────────────────────────────
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
listen [::]:80;
|
|
||||||
server_name ~^s3\.;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
proxy_pass http://s3_backend;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
# Critical: preserve the original Host so RustFS validates
|
|
||||||
# the request against the same host the URL was signed for.
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 80 default_server;
|
|
||||||
listen [::]:80 default_server;
|
|
||||||
server_name _;
|
|
||||||
|
|
||||||
# Note: when you enable TLS, replace this block with a redirect:
|
|
||||||
# return 301 https://$host$request_uri;
|
|
||||||
location / {
|
|
||||||
proxy_pass http://puter_backend;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
proxy_set_header X-Forwarded-Host $host;
|
|
||||||
proxy_set_header X-Forwarded-Port $server_port;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection $connection_upgrade;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── HTTPS (port 443) — uncomment after dropping certs in ./puter/tls/ ─
|
|
||||||
# server {
|
|
||||||
# listen 443 ssl;
|
|
||||||
# listen [::]:443 ssl;
|
|
||||||
# http2 on;
|
|
||||||
# server_name ~^s3\.;
|
|
||||||
#
|
|
||||||
# ssl_certificate /etc/nginx/tls/fullchain.pem;
|
|
||||||
# ssl_certificate_key /etc/nginx/tls/privkey.pem;
|
|
||||||
# ssl_protocols TLSv1.2 TLSv1.3;
|
|
||||||
# ssl_ciphers HIGH:!aNULL:!MD5;
|
|
||||||
# ssl_session_cache shared:SSL:10m;
|
|
||||||
# ssl_session_timeout 10m;
|
|
||||||
#
|
|
||||||
# location / {
|
|
||||||
# proxy_pass http://s3_backend;
|
|
||||||
# proxy_http_version 1.1;
|
|
||||||
# proxy_set_header Host $host;
|
|
||||||
# proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
# proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# server {
|
|
||||||
# listen 443 ssl default_server;
|
|
||||||
# listen [::]:443 ssl default_server;
|
|
||||||
# http2 on;
|
|
||||||
# server_name _;
|
|
||||||
#
|
|
||||||
# ssl_certificate /etc/nginx/tls/fullchain.pem;
|
|
||||||
# ssl_certificate_key /etc/nginx/tls/privkey.pem;
|
|
||||||
# ssl_protocols TLSv1.2 TLSv1.3;
|
|
||||||
# ssl_ciphers HIGH:!aNULL:!MD5;
|
|
||||||
# ssl_session_cache shared:SSL:10m;
|
|
||||||
# ssl_session_timeout 10m;
|
|
||||||
#
|
|
||||||
# location / {
|
|
||||||
# proxy_pass http://puter_backend;
|
|
||||||
# proxy_http_version 1.1;
|
|
||||||
# proxy_set_header Host $host;
|
|
||||||
# proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
# proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
# proxy_set_header X-Forwarded-Host $host;
|
|
||||||
# proxy_set_header X-Forwarded-Port $server_port;
|
|
||||||
# proxy_set_header Upgrade $http_upgrade;
|
|
||||||
# proxy_set_header Connection $connection_upgrade;
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user