mirror of
https://tangled.org/tranquil.farm/tranquil-pds
synced 2026-09-03 00:36:55 +00:00
fix(docs): install docs better
Lewis: May this revision serve well! <lu5a@proton.me>
This commit is contained in:
+1
-1
@@ -50,7 +50,7 @@ RUN apk add --no-cache ca-certificates
|
||||
COPY --from=builder /tmp/tranquil-pds /usr/local/bin/tranquil-pds
|
||||
COPY --from=frontend /app/dist /var/lib/tranquil-pds/frontend
|
||||
WORKDIR /app
|
||||
ENV SERVER_HOST=0.0.0.0
|
||||
ENV SERVER_HOST=[::]
|
||||
ENV SERVER_PORT=3000
|
||||
EXPOSE 3000
|
||||
CMD ["tranquil-pds"]
|
||||
|
||||
@@ -6,7 +6,7 @@ A Personal Data Server for the AT Protocol.
|
||||
|
||||
We came together to make this PDS to enable and empower our users to better host their data on this shared protocol. All of our decisions as a project are guided by their usefulness to the community: PDS hosters and end-users both.
|
||||
|
||||
Comparatively: Bluesky the company created a "reference PDS" that we can self-host quite easily, and that's great, but Bluesky has an incentive to make software for themselvess first & foremost, then secondly their software can be useful for us self-hosters. In contrast, Tranquil is not from a company, and will never be.
|
||||
Comparatively: Bluesky the company created a "reference PDS" that we can self-host quite easily, and that's great, but Bluesky has an incentive to make software for themselves first & foremost, then secondly their software can be useful for us self-hosters. In contrast, Tranquil is not from a company, and will never be.
|
||||
|
||||
## What's different about Tranquil PDS
|
||||
|
||||
@@ -20,7 +20,7 @@ It is a superset of the reference PDS, including:
|
||||
- account delegation: letting others manage an account with configurable permission levels
|
||||
- a built-in web UI for account management, repo browsing, and admin
|
||||
|
||||
Unlike the ref PDS, Tranquil itself is compiled to a single binary with no nodeJS runtime. However, at time of writing, Tranquil requires postgres running separately.
|
||||
Unlike the ref PDS, Tranquil is a single binary with no nodejs runtime. That said, at time of writing, Tranquil does require postgres running separately.
|
||||
|
||||
## Quick Start
|
||||
|
||||
@@ -46,24 +46,28 @@ just test
|
||||
just lint
|
||||
```
|
||||
|
||||
Nix users can enter a devshell with `nix develop`, or `direnv allow` to auto-enter via the bundled `.envrc`. Pre-built artifacts (including the devshell) are available from our [binary cache](docs/install-nix.md#binary-cache).
|
||||
Nix users can enter a devshell with `nix develop`, or `direnv allow` to auto-enter via the bundled `.envrc`. Pre-built artifacts including the devshell are available from our [binary cache](docs/2_INSTALL_NIX.md#binary-cache).
|
||||
|
||||
## Production Deployment
|
||||
|
||||
### Quick Deploy (Docker/Podman Compose)
|
||||
|
||||
Edit `config.toml` with your values. Generate secrets with `openssl rand -base64 48`.
|
||||
`docker-compose.prod.yaml` pulls the prebuilt image `atcr.io/tranquil.farm/tranquil-pds:latest`. Sign in to the registry first with `podman login atcr.io`. The Containers guide covers building from source.
|
||||
|
||||
```bash
|
||||
cp example.toml config.toml
|
||||
```
|
||||
|
||||
Edit `config.toml` with your values and generate secrets with `openssl rand -base64 48`. Set the postgres password to match `docker-compose.prod.yaml`. nginx needs a TLS certificate before it starts, so follow the wildcard cert steps in the [Containers guide](docs/2_INSTALL_CONTAINERS.md).
|
||||
|
||||
```bash
|
||||
podman-compose -f docker-compose.prod.yaml up -d
|
||||
```
|
||||
|
||||
### Installation Guides
|
||||
|
||||
- [Nix](docs/install-nix.md)
|
||||
- [Containers](docs/install-containers.md)
|
||||
- [Kubernetes](docs/install-kubernetes.md)
|
||||
- [Nix](docs/2_INSTALL_NIX.md)
|
||||
- [Containers](docs/2_INSTALL_CONTAINERS.md)
|
||||
|
||||
## Community
|
||||
|
||||
@@ -76,7 +80,7 @@ We currently don't have a shared space to chat and organize Tranquil things, but
|
||||
- [@oyster.cafe](https://tangled.org/did:plc:3fwecdnvtcscjnrx2p4n7alz)
|
||||
- [@nel.pet](https://tangled.org/did:plc:h5wsnqetncv6lu2weom35lg2)
|
||||
|
||||
### Amazing contributers
|
||||
### Amazing contributors
|
||||
|
||||
- [@isabelroses.com](https://tangled.org/did:plc:qxichs7jsycphrsmbujwqbfb)
|
||||
- [@quilling.dev](https://tangled.org/did:plc:jrtgsidnmxaen4offglr5lsh)
|
||||
|
||||
@@ -48,7 +48,7 @@ http {
|
||||
client_max_body_size 10G;
|
||||
|
||||
location /xrpc/ {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_pass http://[::1]:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
@@ -62,18 +62,8 @@ http {
|
||||
proxy_request_buffering off;
|
||||
}
|
||||
|
||||
location = /oauth-client-metadata.json {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header Accept-Encoding "";
|
||||
sub_filter_once off;
|
||||
sub_filter_types application/json;
|
||||
sub_filter '__PDS_HOSTNAME__' $host;
|
||||
}
|
||||
|
||||
location /oauth/ {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_pass http://[::1]:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
@@ -84,7 +74,7 @@ http {
|
||||
}
|
||||
|
||||
location /.well-known/ {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_pass http://[::1]:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
@@ -93,7 +83,7 @@ http {
|
||||
}
|
||||
|
||||
location /webhook/ {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_pass http://[::1]:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
@@ -102,7 +92,7 @@ http {
|
||||
}
|
||||
|
||||
location = /metrics {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_pass http://[::1]:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
@@ -111,25 +101,25 @@ http {
|
||||
}
|
||||
|
||||
location = /health {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_pass http://[::1]:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
location = /robots.txt {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_pass http://[::1]:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
location = /logo {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_pass http://[::1]:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
location ~ ^/u/[^/]+/did\.json$ {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_pass http://[::1]:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
@@ -138,7 +128,7 @@ http {
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_pass http://[::1]:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
name="tranquil-pds-app"
|
||||
description="Tranquil PDS app"
|
||||
|
||||
: "${TRANQUIL_IMAGE:=atcr.io/tranquil.farm/tranquil-pds:latest}"
|
||||
: "${TRANQUIL_DATA:=/srv/tranquil-pds}"
|
||||
: "${TRANQUIL_CONFIG:=/srv/tranquil-pds/config/config.toml}"
|
||||
|
||||
depend() {
|
||||
need tranquil-pds-db
|
||||
}
|
||||
|
||||
start() {
|
||||
ebegin "Starting ${name}"
|
||||
podman container exists tranquil-pds-app && podman rm -f tranquil-pds-app
|
||||
podman run -d --name tranquil-pds-app \
|
||||
--pod tranquil-pds \
|
||||
-e "SERVER_HOST=[::]" \
|
||||
-e SERVER_PORT=3000 \
|
||||
-v "${TRANQUIL_CONFIG}:/etc/tranquil-pds/config.toml:ro,Z" \
|
||||
-v "${TRANQUIL_DATA}/blobs:/var/lib/tranquil-pds/blobs:Z" \
|
||||
-v "${TRANQUIL_DATA}/store:/var/lib/tranquil-pds/store:Z" \
|
||||
"${TRANQUIL_IMAGE}"
|
||||
local rc=$?
|
||||
[ "${rc}" -eq 0 ] || { eend "${rc}"; return "${rc}"; }
|
||||
|
||||
ebegin "Waiting for tranquil-pds to accept connections"
|
||||
local waited=0
|
||||
while [ "${waited}" -lt 60 ]; do
|
||||
podman exec tranquil-pds-app wget -q --spider http://localhost:3000/xrpc/_health && break
|
||||
sleep 1
|
||||
waited=$((waited + 1))
|
||||
done
|
||||
[ "${waited}" -lt 60 ]
|
||||
eend $?
|
||||
}
|
||||
|
||||
stop() {
|
||||
ebegin "Stopping ${name}"
|
||||
podman rm -f tranquil-pds-app
|
||||
eend $?
|
||||
}
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
name="tranquil-pds-db"
|
||||
description="Tranquil PDS postgres"
|
||||
|
||||
: "${TRANQUIL_DB_IMAGE:=docker.io/library/postgres:18-alpine}"
|
||||
: "${TRANQUIL_DATA:=/srv/tranquil-pds}"
|
||||
|
||||
depend() {
|
||||
need tranquil-pds-pod
|
||||
}
|
||||
|
||||
start() {
|
||||
ebegin "Starting ${name}"
|
||||
podman container exists tranquil-pds-db && podman rm -f tranquil-pds-db
|
||||
podman run -d --name tranquil-pds-db \
|
||||
--pod tranquil-pds \
|
||||
-e POSTGRES_USER=tranquil_pds \
|
||||
-e POSTGRES_DB=pds \
|
||||
--secret tranquil-pds-db-password,type=env,target=POSTGRES_PASSWORD \
|
||||
-v "${TRANQUIL_DATA}/postgres:/var/lib/postgresql:Z" \
|
||||
"${TRANQUIL_DB_IMAGE}"
|
||||
local rc=$?
|
||||
[ "${rc}" -eq 0 ] || { eend "${rc}"; return "${rc}"; }
|
||||
|
||||
ebegin "Waiting for postgres to accept connections"
|
||||
local waited=0
|
||||
while [ "${waited}" -lt 30 ]; do
|
||||
podman exec tranquil-pds-db pg_isready -U tranquil_pds -d pds >/dev/null 2>&1 && break
|
||||
sleep 1
|
||||
waited=$((waited + 1))
|
||||
done
|
||||
[ "${waited}" -lt 30 ]
|
||||
eend $?
|
||||
}
|
||||
|
||||
stop() {
|
||||
ebegin "Stopping ${name}"
|
||||
podman rm -f tranquil-pds-db
|
||||
eend $?
|
||||
}
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
name="tranquil-pds-nginx"
|
||||
description="Tranquil PDS nginx reverse proxy"
|
||||
|
||||
: "${TRANQUIL_NGINX_IMAGE:=docker.io/library/nginx:1.29-alpine}"
|
||||
: "${TRANQUIL_DATA:=/srv/tranquil-pds}"
|
||||
: "${TRANQUIL_CONFIG_DIR:=/srv/tranquil-pds/config}"
|
||||
|
||||
depend() {
|
||||
need tranquil-pds-app
|
||||
}
|
||||
|
||||
start() {
|
||||
ebegin "Starting ${name}"
|
||||
podman container exists tranquil-pds-nginx && podman rm -f tranquil-pds-nginx
|
||||
podman run -d --name tranquil-pds-nginx \
|
||||
--pod tranquil-pds \
|
||||
-v "${TRANQUIL_CONFIG_DIR}/nginx.conf:/etc/nginx/nginx.conf:ro,Z" \
|
||||
-v "${TRANQUIL_DATA}/certs:/etc/nginx/certs:ro,Z" \
|
||||
-v "${TRANQUIL_DATA}/acme:/var/www/acme:ro,Z" \
|
||||
"${TRANQUIL_NGINX_IMAGE}"
|
||||
eend $?
|
||||
}
|
||||
|
||||
stop() {
|
||||
ebegin "Stopping ${name}"
|
||||
podman rm -f tranquil-pds-nginx
|
||||
eend $?
|
||||
}
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
name="tranquil-pds-pod"
|
||||
description="Tranquil PDS podman pod"
|
||||
|
||||
depend() {
|
||||
need net
|
||||
after firewall
|
||||
}
|
||||
|
||||
start() {
|
||||
ebegin "Creating ${name}"
|
||||
podman pod exists tranquil-pds || \
|
||||
podman pod create --name tranquil-pds \
|
||||
--publish 80:80 \
|
||||
--publish 443:443
|
||||
eend $?
|
||||
}
|
||||
|
||||
stop() {
|
||||
ebegin "Removing ${name}"
|
||||
podman pod rm -f tranquil-pds
|
||||
eend $?
|
||||
}
|
||||
@@ -3,9 +3,9 @@ Description=Tranquil PDS AT Protocol PDS
|
||||
After=tranquil-pds-db.service
|
||||
[Container]
|
||||
ContainerName=tranquil-pds-app
|
||||
Image=localhost/tranquil-pds:latest
|
||||
Image=atcr.io/tranquil.farm/tranquil-pds:latest
|
||||
Pod=tranquil-pds.pod
|
||||
Environment=SERVER_HOST=0.0.0.0
|
||||
Environment=SERVER_HOST=[::]
|
||||
Environment=SERVER_PORT=3000
|
||||
Volume=/srv/tranquil-pds/config/config.toml:/etc/tranquil-pds/config.toml:ro,Z
|
||||
Volume=/srv/tranquil-pds/blobs:/var/lib/tranquil-pds/blobs:Z
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
[Unit]
|
||||
Description=Tranquil PDS frontend
|
||||
After=tranquil-pds-app.service
|
||||
|
||||
[Container]
|
||||
ContainerName=tranquil-pds-frontend
|
||||
Image=localhost/tranquil-pds-frontend:latest
|
||||
Pod=tranquil-pds.pod
|
||||
Volume=/opt/tranquil-pds/frontend/nginx-quadlet.conf:/etc/nginx/conf.d/default.conf:ro,Z
|
||||
HealthCmd=wget -q --spider http://localhost:8080/
|
||||
HealthInterval=30s
|
||||
HealthTimeout=10s
|
||||
HealthRetries=3
|
||||
HealthStartPeriod=5s
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -1,9 +1,9 @@
|
||||
[Unit]
|
||||
Description=Tranquil PDS nginx reverse proxy
|
||||
After=tranquil-pds-app.service tranquil-pds-frontend.service
|
||||
After=tranquil-pds-app.service
|
||||
[Container]
|
||||
ContainerName=tranquil-pds-nginx
|
||||
Image=docker.io/library/nginx:1.28-alpine
|
||||
Image=docker.io/library/nginx:1.29-alpine
|
||||
Pod=tranquil-pds.pod
|
||||
Volume=/srv/tranquil-pds/config/nginx.conf:/etc/nginx/nginx.conf:ro,Z
|
||||
Volume=/srv/tranquil-pds/certs:/etc/nginx/certs:ro,Z
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
services:
|
||||
tranquil-pds:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
image: tranquil-pds:latest
|
||||
image: atcr.io/tranquil.farm/tranquil-pds:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
SERVER_HOST: "0.0.0.0"
|
||||
SERVER_HOST: "[::]"
|
||||
volumes:
|
||||
- ./config.toml:/etc/tranquil-pds/config.toml:ro
|
||||
# In memory of @olaren.dev's blobs when lewis forgot to update /tranquil to /tranquil-pds :(
|
||||
|
||||
+1
-12
@@ -29,7 +29,7 @@ services:
|
||||
cargo watch -x "run -p tranquil-server --features native-tls-roots -- --config /app/config.dev.toml"
|
||||
'
|
||||
environment:
|
||||
SERVER_HOST: "0.0.0.0"
|
||||
SERVER_HOST: "[::]"
|
||||
SQLX_OFFLINE: "true"
|
||||
PLC_DIRECTORY_URL: "http://plc:2582"
|
||||
volumes:
|
||||
@@ -46,17 +46,6 @@ services:
|
||||
plc:
|
||||
condition: service_started
|
||||
|
||||
frontend:
|
||||
profiles: [prod]
|
||||
build:
|
||||
context: ./frontend
|
||||
dockerfile: Dockerfile
|
||||
image: tranquil-pds-frontend
|
||||
ports:
|
||||
- "8080:80"
|
||||
depends_on:
|
||||
- app
|
||||
|
||||
frontend-dev:
|
||||
profiles: [dev]
|
||||
image: node:24-alpine
|
||||
|
||||
+213
-294
@@ -2,17 +2,27 @@
|
||||
|
||||
This guide covers deploying Tranquil PDS using containers with podman.
|
||||
|
||||
- **Debian 13+**: Uses systemd quadlets (modern, declarative container management)
|
||||
- **Alpine 3.23+**: Uses OpenRC service script with podman-compose
|
||||
- **Debian 13+**: Uses systemd quadlets
|
||||
- **Alpine 3.23+**: Uses per-container OpenRC init scripts
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A server :p
|
||||
- Disk space for blobs (depends on usage; plan for ~1GB per active user as a baseline)
|
||||
- Disk space for blobs, around* 1GB per active user as a baseline
|
||||
- A domain name pointing to your server's IP
|
||||
- A **wildcard TLS certificate** for `*.pds.example.com` (user handles are served as subdomains)
|
||||
- A **wildcard TLS certificate** for `*.pds.example.com`, since user handles are served as subdomains
|
||||
- Root/sudo/doas access
|
||||
|
||||
> 🦪 Lewis
|
||||
>
|
||||
> * "around" here meaning "at absolute least!"
|
||||
|
||||
## Reverse proxy
|
||||
|
||||
The bundled pod ships nginx as its reverse proxy, and this guide uses it throughout. nginx is just the default. Swap in whatever you prefer. Tranquil serves its API and web UI on a single port, so any reverse proxy works once it forwards to the app on `[::1]:3000`.
|
||||
|
||||
Caddy is one good option. It grabs & renews TLS certificates automatically, including the wildcard this setup needs (if you're lucky, which Lewis is not), so the manual certbot steps later become unnecessary. We plan to soon also have the ability to terminate TLS directly in-Tranquil so that there's no reverse proxy needed.
|
||||
|
||||
## Quickstart (docker/podman compose)
|
||||
|
||||
If you just want to get running quickly:
|
||||
@@ -23,66 +33,55 @@ cp example.toml config.toml
|
||||
|
||||
Edit `config.toml` with your values. Generate secrets with `openssl rand -base64 48`.
|
||||
|
||||
Build and start:
|
||||
`docker-compose.prod.yaml` pulls the prebuilt image from atcr.io. Sign in to the registry first with `podman login atcr.io`.
|
||||
|
||||
nginx will not start without a certificate, so create a temporary self-signed one, bring the stack up, then swap in a real wildcard cert:
|
||||
```sh
|
||||
podman build -t tranquil-pds:latest .
|
||||
podman build -t tranquil-pds-frontend:latest ./frontend
|
||||
mkdir -p certs
|
||||
openssl req -x509 -nodes -days 1 -newkey rsa:2048 \
|
||||
-keyout certs/privkey.pem -out certs/fullchain.pem \
|
||||
-subj "/CN=pds.example.com"
|
||||
podman-compose -f docker-compose.prod.yaml up -d
|
||||
```
|
||||
|
||||
Get initial certificate (after DNS is configured):
|
||||
To build the image from source instead, run `podman build -t atcr.io/tranquil.farm/tranquil-pds:latest .` before bringing the stack up.
|
||||
|
||||
User handles are subdomains, so the real certificate must be a wildcard for `*.pds.example.com`, which requires DNS-01 validation. Follow the DNS cert steps in the Wildcard TLS certificate section below, then:
|
||||
```sh
|
||||
podman-compose -f docker-compose.prod.yaml run --rm certbot certonly \
|
||||
--webroot -w /var/www/acme -d pds.example.com -d '*.pds.example.com'
|
||||
ln -sf live/pds.example.com/fullchain.pem certs/fullchain.pem
|
||||
ln -sf live/pds.example.com/privkey.pem certs/privkey.pem
|
||||
podman-compose -f docker-compose.prod.yaml restart nginx
|
||||
```
|
||||
|
||||
The end!!!
|
||||
## Standalone container without compose
|
||||
|
||||
Or wait, you want more? Perhaps a deployment that comes back on server restart?
|
||||
If you already have postgres running on the host, you can run just the app container.
|
||||
|
||||
For production setups with proper service management, continue to either the Debian or Alpine section below.
|
||||
|
||||
## Standalone containers (no compose)
|
||||
|
||||
If you already have postgres running on the host, you can run just the app containers.
|
||||
|
||||
Build the images:
|
||||
Pull the image. atcr.io requires authentication, so sign in first:
|
||||
```sh
|
||||
podman build -t tranquil-pds:latest .
|
||||
podman build -t tranquil-pds-frontend:latest ./frontend
|
||||
podman login atcr.io
|
||||
podman pull atcr.io/tranquil.farm/tranquil-pds:latest
|
||||
```
|
||||
|
||||
Run the backend with host networking (so it can access postgres on localhost) and mount the blob storage:
|
||||
Run with host networking so it can reach postgres on localhost, and mount config + storage:
|
||||
```sh
|
||||
podman run -d --name tranquil-pds \
|
||||
--network=host \
|
||||
-v /etc/tranquil-pds/config.toml:/etc/tranquil-pds/config.toml:ro,Z \
|
||||
-v /var/lib/tranquil-pds:/var/lib/tranquil-pds:Z \
|
||||
tranquil-pds:latest
|
||||
atcr.io/tranquil.farm/tranquil-pds:latest
|
||||
```
|
||||
|
||||
Run the frontend with port mapping (the container's nginx listens on port 80):
|
||||
```sh
|
||||
podman run -d --name tranquil-pds-frontend \
|
||||
-p 8080:80 \
|
||||
tranquil-pds-frontend:latest
|
||||
```
|
||||
To build from source instead, run `podman build -t atcr.io/tranquil.farm/tranquil-pds:latest .` and use that tag.
|
||||
|
||||
Then configure your host nginx to proxy to both containers. Replace the static file `try_files` directives with proxy passes:
|
||||
Then point your reverse proxy at the app on port 3000 for every route. With nginx that looks like:
|
||||
|
||||
```nginx
|
||||
# API routes to backend
|
||||
location /xrpc/ {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
# ... (see Debian guide for full proxy headers)
|
||||
proxy_pass http://[::1]:3000;
|
||||
# full proxy headers are in deploy/nginx/nginx-pod.conf
|
||||
}
|
||||
|
||||
# Static routes to frontend container
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_pass http://[::1]:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
@@ -91,7 +90,98 @@ location / {
|
||||
}
|
||||
```
|
||||
|
||||
See the Debian with systemd quadlets section below for the full nginx config with all API routes.
|
||||
With Caddy the equivalent is a one-line `reverse_proxy [::1]:3000`, and it handles TLS for you. See `deploy/nginx/nginx-pod.conf` in the repo for the full nginx config with all routes.
|
||||
|
||||
The end!!!
|
||||
|
||||
Or wait, you want more? Perhaps a deployment that comes back on server restart?
|
||||
|
||||
---
|
||||
|
||||
# Common setup
|
||||
|
||||
Both service-managed deployments share the steps below. Do these first, then jump to the Debian or Alpine section for the init-specific stuff, and finish in the Wildcard TLS certificate section.
|
||||
|
||||
## Install podman
|
||||
|
||||
**Debian:**
|
||||
```bash
|
||||
apt update
|
||||
apt install -y podman
|
||||
```
|
||||
|
||||
**Alpine:**
|
||||
```sh
|
||||
apk update
|
||||
apk add podman fuse-overlayfs
|
||||
rc-update add cgroups
|
||||
rc-service cgroups start
|
||||
```
|
||||
|
||||
## Create the directory structure
|
||||
|
||||
```sh
|
||||
mkdir -p /srv/tranquil-pds/{postgres,blobs,store,certs,acme,config}
|
||||
```
|
||||
|
||||
## Clone the repo and pull the image
|
||||
|
||||
The repo provides the quadlet, OpenRC, and nginx files. The image comes prebuilt from atcr.io. Sign in before pulling:
|
||||
```sh
|
||||
cd /opt
|
||||
git clone https://tangled.org/tranquil.farm/tranquil-pds tranquil-pds
|
||||
podman login atcr.io
|
||||
podman pull atcr.io/tranquil.farm/tranquil-pds:latest
|
||||
```
|
||||
|
||||
To build from source instead, tag it with the same name so the service uses it:
|
||||
```sh
|
||||
cd tranquil-pds && podman build -t atcr.io/tranquil.farm/tranquil-pds:latest .
|
||||
```
|
||||
|
||||
## Create a configuration file
|
||||
|
||||
```sh
|
||||
cp /opt/tranquil-pds/example.toml /srv/tranquil-pds/config/config.toml
|
||||
chmod 600 /srv/tranquil-pds/config/config.toml
|
||||
```
|
||||
|
||||
Edit `/srv/tranquil-pds/config/config.toml` and fill in your values. Generate secrets with:
|
||||
```sh
|
||||
openssl rand -base64 48
|
||||
```
|
||||
|
||||
> 🦪 Lewis
|
||||
>
|
||||
> Every config option can also be set via an environment variable
|
||||
> named in the comments in `example.toml`. Environment variables always take
|
||||
> precedence over the config file.
|
||||
|
||||
## Create the nginx config and database secret
|
||||
|
||||
The bundled pod runs nginx as its proxy. The app and nginx share the pod's network namespace, so the proxy reaches the app on `[::1]:3000`:
|
||||
```sh
|
||||
cp /opt/tranquil-pds/deploy/nginx/nginx-pod.conf /srv/tranquil-pds/config/nginx.conf
|
||||
```
|
||||
|
||||
Create the podman secret for the database password. Use the same password in the `database.url` of your `config.toml`:
|
||||
```sh
|
||||
echo "$DB_PASSWORD" | podman secret create tranquil-pds-db-password -
|
||||
```
|
||||
|
||||
## Create a temporary TLS certificate
|
||||
|
||||
The nginx that comes out of the box will not start without a certificate. The real wildcard cert needs DNS-01 validation and is set up once the stack is running, so for now let's add a self-signed placeholder so the services can start:
|
||||
```sh
|
||||
openssl req -x509 -nodes -days 1 -newkey rsa:2048 \
|
||||
-keyout /srv/tranquil-pds/certs/privkey.pem \
|
||||
-out /srv/tranquil-pds/certs/fullchain.pem \
|
||||
-subj "/CN=pds.example.com"
|
||||
```
|
||||
|
||||
If your proxy obtains its own certificates, like Caddy does, you can skip this step!
|
||||
|
||||
The app runs migrations itself on first boot btw, so there is no separate migration step if you're looking for one.
|
||||
|
||||
---
|
||||
|
||||
@@ -99,117 +189,32 @@ See the Debian with systemd quadlets section below for the full nginx config wit
|
||||
|
||||
Quadlets are a nice way to run podman containers under systemd.
|
||||
|
||||
## Install podman
|
||||
|
||||
```bash
|
||||
apt update
|
||||
apt install -y podman
|
||||
```
|
||||
|
||||
## Create the directory structure
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/containers/systemd
|
||||
mkdir -p /srv/tranquil-pds/{postgres,blobs,store,certs,acme,config}
|
||||
```
|
||||
|
||||
## Create a configuration file
|
||||
|
||||
```bash
|
||||
cp /opt/tranquil-pds/example.toml /srv/tranquil-pds/config/config.toml
|
||||
chmod 600 /srv/tranquil-pds/config/config.toml
|
||||
```
|
||||
|
||||
Edit `/srv/tranquil-pds/config/config.toml` and fill in your values. Generate secrets with:
|
||||
```bash
|
||||
openssl rand -base64 48
|
||||
```
|
||||
|
||||
> **Note:** Every config option can also be set via environment variables
|
||||
> (see comments in `example.toml`). Environment variables always take
|
||||
> precedence over the config file.
|
||||
|
||||
## Install quadlet definitions
|
||||
|
||||
Copy the quadlet files from the repository:
|
||||
```bash
|
||||
mkdir -p /etc/containers/systemd
|
||||
cp /opt/tranquil-pds/deploy/quadlets/tranquil-pds.pod /etc/containers/systemd/
|
||||
cp /opt/tranquil-pds/deploy/quadlets/tranquil-pds-db.container /etc/containers/systemd/
|
||||
cp /opt/tranquil-pds/deploy/quadlets/tranquil-pds-app.container /etc/containers/systemd/
|
||||
cp /opt/tranquil-pds/deploy/quadlets/tranquil-pds-frontend.container /etc/containers/systemd/
|
||||
cp /opt/tranquil-pds/deploy/quadlets/tranquil-pds-nginx.container /etc/containers/systemd/
|
||||
```
|
||||
|
||||
Optional quadlets for valkey and minio are also available in `deploy/quadlets/` if you need them.
|
||||
|
||||
## Create nginx configuration
|
||||
|
||||
```bash
|
||||
cp /opt/tranquil-pds/nginx.conf /srv/tranquil-pds/config/nginx.conf
|
||||
```
|
||||
|
||||
## Clone and build images
|
||||
|
||||
```bash
|
||||
cd /opt
|
||||
git clone https://tangled.org/tranquil.farm/tranquil-pds tranquil-pds
|
||||
cd tranquil-pds
|
||||
podman build -t tranquil-pds:latest .
|
||||
podman build -t tranquil-pds-frontend:latest ./frontend
|
||||
```
|
||||
|
||||
## Create podman secrets
|
||||
|
||||
```bash
|
||||
echo "$DB_PASSWORD" | podman secret create tranquil-pds-db-password -
|
||||
```
|
||||
|
||||
## Start services and initialize
|
||||
## Start services
|
||||
|
||||
```bash
|
||||
systemctl daemon-reload
|
||||
systemctl start tranquil-pds-db
|
||||
sleep 10
|
||||
```
|
||||
|
||||
## Obtain a wildcard SSL cert
|
||||
|
||||
User handles are served as subdomains (eg. `alice.pds.example.com`), so you need a wildcard certificate. Wildcard certs require DNS-01 validation.
|
||||
|
||||
Create temporary self-signed cert to start services:
|
||||
```bash
|
||||
openssl req -x509 -nodes -days 1 -newkey rsa:2048 \
|
||||
-keyout /srv/tranquil-pds/certs/privkey.pem \
|
||||
-out /srv/tranquil-pds/certs/fullchain.pem \
|
||||
-subj "/CN=pds.example.com"
|
||||
systemctl start tranquil-pds-app tranquil-pds-frontend tranquil-pds-nginx
|
||||
```
|
||||
|
||||
Get a wildcard certificate using DNS validation:
|
||||
```bash
|
||||
podman run --rm -it \
|
||||
-v /srv/tranquil-pds/certs:/etc/letsencrypt:Z \
|
||||
docker.io/certbot/certbot:v5.2.2 certonly \
|
||||
--manual --preferred-challenges dns \
|
||||
-d pds.example.com -d '*.pds.example.com' \
|
||||
--agree-tos --email you@example.com
|
||||
```
|
||||
|
||||
Follow the prompts to add TXT records to your DNS. Note: manual mode doesn't auto-renew.
|
||||
|
||||
For automated renewal, use a DNS provider plugin (eg. cloudflare, route53).
|
||||
|
||||
Link certificates and restart:
|
||||
```bash
|
||||
ln -sf /srv/tranquil-pds/certs/live/pds.example.com/fullchain.pem /srv/tranquil-pds/certs/fullchain.pem
|
||||
ln -sf /srv/tranquil-pds/certs/live/pds.example.com/privkey.pem /srv/tranquil-pds/certs/privkey.pem
|
||||
systemctl restart tranquil-pds-nginx
|
||||
systemctl start tranquil-pds-app tranquil-pds-nginx
|
||||
```
|
||||
|
||||
## Enable all services
|
||||
|
||||
```bash
|
||||
systemctl enable tranquil-pds-db tranquil-pds-app tranquil-pds-frontend tranquil-pds-nginx
|
||||
systemctl enable tranquil-pds-db tranquil-pds-app tranquil-pds-nginx
|
||||
```
|
||||
|
||||
## Configure firewall if you're into that sort of thing
|
||||
@@ -222,165 +227,38 @@ ufw allow 443/tcp
|
||||
ufw enable
|
||||
```
|
||||
|
||||
## Cert renewal
|
||||
|
||||
Add to root's crontab (`crontab -e`):
|
||||
```
|
||||
0 0 * * * podman run --rm -v /srv/tranquil-pds/certs:/etc/letsencrypt:Z -v /srv/tranquil-pds/acme:/var/www/acme:Z docker.io/certbot/certbot:v5.2.2 renew --quiet && systemctl reload tranquil-pds-nginx
|
||||
```
|
||||
Now finish in the Wildcard TLS certificate section below.
|
||||
|
||||
---
|
||||
|
||||
# Alpine with OpenRC
|
||||
|
||||
Alpine uses OpenRC, not systemd. So instead of quadlets we'll use podman-compose with an OpenRC service wrapper.
|
||||
Alpine uses OpenRC, not systemd, bless its soul. So instead of quadlets we use a set of OpenRC init scripts, one per container.
|
||||
|
||||
## Install podman
|
||||
## Install the OpenRC services
|
||||
|
||||
Copy the init scripts. They run the pod, postgres, the app, and nginx as separate services ordered with `depend()`:
|
||||
```sh
|
||||
apk update
|
||||
apk add podman podman-compose fuse-overlayfs cni-plugins
|
||||
rc-update add cgroups
|
||||
rc-service cgroups start
|
||||
cp /opt/tranquil-pds/deploy/openrc/tranquil-pds-pod /etc/init.d/
|
||||
cp /opt/tranquil-pds/deploy/openrc/tranquil-pds-db /etc/init.d/
|
||||
cp /opt/tranquil-pds/deploy/openrc/tranquil-pds-app /etc/init.d/
|
||||
cp /opt/tranquil-pds/deploy/openrc/tranquil-pds-nginx /etc/init.d/
|
||||
chmod +x /etc/init.d/tranquil-pds-pod /etc/init.d/tranquil-pds-db /etc/init.d/tranquil-pds-app /etc/init.d/tranquil-pds-nginx
|
||||
```
|
||||
|
||||
Enable podman socket for compose:
|
||||
The scripts default to `/srv/tranquil-pds` for data and `/srv/tranquil-pds/config/config.toml` for config. Override via `/etc/conf.d/tranquil-pds-app` and friends if your paths differ.
|
||||
|
||||
## Start services
|
||||
|
||||
Starting the nginx service pulls in the pod, postgres, and app through its dependencies:
|
||||
```sh
|
||||
rc-update add podman
|
||||
rc-service podman start
|
||||
rc-service tranquil-pds-nginx start
|
||||
```
|
||||
|
||||
## Create the directory structure
|
||||
## Enable services at boot time
|
||||
|
||||
```sh
|
||||
mkdir -p /srv/tranquil-pds/{data,config}
|
||||
mkdir -p /srv/tranquil-pds/data/{postgres,blobs,certs,acme}
|
||||
```
|
||||
|
||||
## Clone the repo and build images
|
||||
|
||||
```sh
|
||||
cd /opt
|
||||
git clone https://tangled.org/tranquil.farm/tranquil-pds tranquil-pds
|
||||
cd tranquil-pds
|
||||
podman build -t tranquil-pds:latest .
|
||||
podman build -t tranquil-pds-frontend:latest ./frontend
|
||||
```
|
||||
|
||||
## Create a configuration file
|
||||
|
||||
```sh
|
||||
cp /opt/tranquil-pds/example.toml /srv/tranquil-pds/config/config.toml
|
||||
chmod 600 /srv/tranquil-pds/config/config.toml
|
||||
```
|
||||
|
||||
Edit `/srv/tranquil-pds/config/config.toml` and fill in your values. Generate secrets with:
|
||||
```sh
|
||||
openssl rand -base64 48
|
||||
```
|
||||
|
||||
> **Note:** Every config option can also be set via environment variables
|
||||
> (see comments in `example.toml`). Environment variables always take
|
||||
> precedence over the config file.
|
||||
|
||||
## Set up compose and nginx
|
||||
|
||||
Copy the production compose and nginx configs:
|
||||
```sh
|
||||
cp /opt/tranquil-pds/docker-compose.prod.yaml /srv/tranquil-pds/docker-compose.yml
|
||||
cp /opt/tranquil-pds/nginx.conf /srv/tranquil-pds/config/nginx.conf
|
||||
```
|
||||
|
||||
Edit `/srv/tranquil-pds/docker-compose.yml` to adjust paths if needed:
|
||||
- Update volume mounts to use `/srv/tranquil-pds/data/` paths
|
||||
- Update nginx config path to `/srv/tranquil-pds/config/nginx.conf`
|
||||
|
||||
Edit `/srv/tranquil-pds/config/nginx.conf` to update cert paths:
|
||||
- Change `/etc/nginx/certs/live/${PDS_HOSTNAME}/` to `/etc/nginx/certs/`
|
||||
|
||||
## Create OpenRC service
|
||||
|
||||
```sh
|
||||
cat > /etc/init.d/tranquil-pds << 'EOF'
|
||||
#!/sbin/openrc-run
|
||||
name="tranquil-pds"
|
||||
description="Tranquil PDS AT Protocol PDS"
|
||||
command="/usr/bin/podman-compose"
|
||||
command_args="-f /srv/tranquil-pds/docker-compose.yml up"
|
||||
command_background=true
|
||||
pidfile="/run/${RC_SVCNAME}.pid"
|
||||
directory="/srv/tranquil-pds"
|
||||
depend() {
|
||||
need net podman
|
||||
after firewall
|
||||
}
|
||||
start_pre() {
|
||||
checkpath -d /srv/tranquil-pds
|
||||
}
|
||||
stop() {
|
||||
ebegin "Stopping ${name}"
|
||||
cd /srv/tranquil-pds
|
||||
podman-compose -f /srv/tranquil-pds/docker-compose.yml down
|
||||
eend $?
|
||||
}
|
||||
EOF
|
||||
chmod +x /etc/init.d/tranquil-pds
|
||||
```
|
||||
|
||||
## Initialize services
|
||||
|
||||
Start services:
|
||||
```sh
|
||||
rc-service tranquil-pds start
|
||||
sleep 15
|
||||
```
|
||||
|
||||
Run migrations:
|
||||
```sh
|
||||
apk add rustup
|
||||
rustup-init -y
|
||||
source ~/.cargo/env
|
||||
cargo install sqlx-cli --no-default-features --features postgres
|
||||
DB_IP=$(podman inspect tranquil-pds-db-1 --format '{{.NetworkSettings.Networks.tranquil-pds_default.IPAddress}}')
|
||||
DATABASE_URL="postgres://tranquil_pds:$DB_PASSWORD@$DB_IP:5432/pds" sqlx migrate run --source /opt/tranquil-pds/migrations
|
||||
```
|
||||
|
||||
## Obtain wildcard SSL cert
|
||||
|
||||
User handles are served as subdomains (eg. `alice.pds.example.com`), so you need a wildcard certificate. Wildcard certs require DNS-01 validation.
|
||||
|
||||
Create temporary self-signed cert to start services:
|
||||
```sh
|
||||
openssl req -x509 -nodes -days 1 -newkey rsa:2048 \
|
||||
-keyout /srv/tranquil-pds/data/certs/privkey.pem \
|
||||
-out /srv/tranquil-pds/data/certs/fullchain.pem \
|
||||
-subj "/CN=pds.example.com"
|
||||
rc-service tranquil-pds restart
|
||||
```
|
||||
|
||||
Get a wildcard certificate using DNS validation:
|
||||
```sh
|
||||
podman run --rm -it \
|
||||
-v /srv/tranquil-pds/data/certs:/etc/letsencrypt \
|
||||
docker.io/certbot/certbot:v5.2.2 certonly \
|
||||
--manual --preferred-challenges dns \
|
||||
-d pds.example.com -d '*.pds.example.com' \
|
||||
--agree-tos --email you@example.com
|
||||
```
|
||||
|
||||
Follow the prompts to add TXT records to your DNS. Note: manual mode doesn't auto-renew.
|
||||
|
||||
Link certificates and restart:
|
||||
```sh
|
||||
ln -sf /srv/tranquil-pds/data/certs/live/pds.example.com/fullchain.pem /srv/tranquil-pds/data/certs/fullchain.pem
|
||||
ln -sf /srv/tranquil-pds/data/certs/live/pds.example.com/privkey.pem /srv/tranquil-pds/data/certs/privkey.pem
|
||||
rc-service tranquil-pds restart
|
||||
```
|
||||
|
||||
## Enable service at boot time
|
||||
|
||||
```sh
|
||||
rc-update add tranquil-pds
|
||||
rc-update add tranquil-pds-pod tranquil-pds-db tranquil-pds-app tranquil-pds-nginx
|
||||
```
|
||||
|
||||
## Configure firewall if you're into that sort of thing
|
||||
@@ -405,11 +283,58 @@ rc-update add ip6tables
|
||||
/etc/init.d/ip6tables save
|
||||
```
|
||||
|
||||
Now finish in the Wildcard TLS certificate section below.
|
||||
|
||||
---
|
||||
|
||||
# Wildcard TLS certificate
|
||||
|
||||
This section sets up the real certificate for the bundled nginx.
|
||||
|
||||
With the stack running behind the temporary self-signed certificate, swap in a real wildcard cert. User handles are served as subdomains like `nel.pds.example.com`, so the certificate must cover `*.pds.example.com`, which requires DNS-01 validation.
|
||||
|
||||
Get a wildcard certificate using DNS validation:
|
||||
```sh
|
||||
podman run --rm -it \
|
||||
-v /srv/tranquil-pds/certs:/etc/letsencrypt:Z \
|
||||
docker.io/certbot/certbot:v5.2.2 certonly \
|
||||
--manual --preferred-challenges dns \
|
||||
-d pds.example.com -d '*.pds.example.com' \
|
||||
--agree-tos --email you@example.com
|
||||
```
|
||||
|
||||
Follow the prompts to add TXT records to your DNS. Note: manual mode doesn't auto-renew. For automated renewal, use a DNS provider plugin or something.
|
||||
|
||||
Link the certificates into place:
|
||||
```sh
|
||||
ln -sf /srv/tranquil-pds/certs/live/pds.example.com/fullchain.pem /srv/tranquil-pds/certs/fullchain.pem
|
||||
ln -sf /srv/tranquil-pds/certs/live/pds.example.com/privkey.pem /srv/tranquil-pds/certs/privkey.pem
|
||||
```
|
||||
|
||||
Restart nginx to load them:
|
||||
|
||||
**Debian:**
|
||||
```bash
|
||||
systemctl restart tranquil-pds-nginx
|
||||
```
|
||||
|
||||
**Alpine:**
|
||||
```sh
|
||||
rc-service tranquil-pds-nginx restart
|
||||
```
|
||||
|
||||
## Cert renewal
|
||||
|
||||
Add to root's crontab (`crontab -e`):
|
||||
Manual mode doesn't auto-renew, so add a renewal job to root's crontab with `crontab -e`.
|
||||
|
||||
**Debian:**
|
||||
```
|
||||
0 0 * * * podman run --rm -v /srv/tranquil-pds/data/certs:/etc/letsencrypt -v /srv/tranquil-pds/data/acme:/var/www/acme docker.io/certbot/certbot:v5.2.2 renew --quiet && rc-service tranquil-pds restart
|
||||
0 0 * * * podman run --rm -v /srv/tranquil-pds/certs:/etc/letsencrypt:Z -v /srv/tranquil-pds/acme:/var/www/acme:Z docker.io/certbot/certbot:v5.2.2 renew --quiet && systemctl reload tranquil-pds-nginx
|
||||
```
|
||||
|
||||
**Alpine:**
|
||||
```
|
||||
0 0 * * * podman run --rm -v /srv/tranquil-pds/certs:/etc/letsencrypt -v /srv/tranquil-pds/acme:/var/www/acme docker.io/certbot/certbot:v5.2.2 renew --quiet && rc-service tranquil-pds-nginx restart
|
||||
```
|
||||
|
||||
---
|
||||
@@ -429,55 +354,49 @@ curl -s https://pds.example.com/.well-known/atproto-did
|
||||
```bash
|
||||
journalctl -u tranquil-pds-app -f
|
||||
podman logs -f tranquil-pds-app
|
||||
podman logs -f tranquil-pds-frontend
|
||||
```
|
||||
|
||||
**Alpine:**
|
||||
```sh
|
||||
podman-compose -f /srv/tranquil-pds/docker-compose.yml logs -f
|
||||
podman logs -f tranquil-pds-tranquil-pds-1
|
||||
podman logs -f tranquil-pds-frontend-1
|
||||
rc-service tranquil-pds-app status
|
||||
podman logs -f tranquil-pds-app
|
||||
```
|
||||
|
||||
## Update Tranquil PDS
|
||||
|
||||
Pull the latest image:
|
||||
```sh
|
||||
cd /opt/tranquil-pds
|
||||
git pull
|
||||
podman build -t tranquil-pds:latest .
|
||||
podman build -t tranquil-pds-frontend:latest ./frontend
|
||||
podman login atcr.io
|
||||
podman pull atcr.io/tranquil.farm/tranquil-pds:latest
|
||||
```
|
||||
|
||||
Debian:
|
||||
```bash
|
||||
systemctl restart tranquil-pds-app tranquil-pds-frontend
|
||||
systemctl restart tranquil-pds-app
|
||||
```
|
||||
|
||||
Alpine:
|
||||
```sh
|
||||
rc-service tranquil-pds restart
|
||||
rc-service tranquil-pds-app restart
|
||||
```
|
||||
|
||||
To update a source-built deployment, `git pull` in the repo and rebuild with `podman build -t atcr.io/tranquil.farm/tranquil-pds:latest .` before restarting.
|
||||
|
||||
## Backup database
|
||||
|
||||
**Debian:**
|
||||
```bash
|
||||
podman exec tranquil-pds-db pg_dump -U tranquil_pds pds > /var/backups/pds-$(date +%Y%m%d).sql
|
||||
```
|
||||
|
||||
**Alpine:**
|
||||
```sh
|
||||
podman exec tranquil-pds-db-1 pg_dump -U tranquil_pds pds > /var/backups/pds-$(date +%Y%m%d).sql
|
||||
podman exec tranquil-pds-db pg_dump -U tranquil_pds pds > /var/backups/pds-$(date +%Y%m%d).sql
|
||||
```
|
||||
|
||||
## Custom homepage
|
||||
|
||||
The frontend container serves `homepage.html` as the landing page. To customize it, either:
|
||||
If a `homepage.html` exists in the app's frontend directory it is served at `/`. The account dashboard stays at `/app/`. The directory defaults to `/var/lib/tranquil-pds/frontend` and is set by `FRONTEND_DIR`. Mount your own into the app container:
|
||||
|
||||
1. Build a custom frontend image with your own `homepage.html`
|
||||
2. Mount a custom `homepage.html` into the frontend container
|
||||
```sh
|
||||
-v /srv/tranquil-pds/homepage.html:/var/lib/tranquil-pds/frontend/homepage.html:ro,Z
|
||||
```
|
||||
|
||||
Example custom homepage:
|
||||
For ex:
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
# Tranquil PDS on kubernetes
|
||||
|
||||
If you're reaching for kubernetes for this app, you're experienced enough to know how to spin up:
|
||||
|
||||
- cloudnativepg (or your preferred postgres operator)
|
||||
- a PersistentVolume for blob storage
|
||||
- the app itself (it's just a container with some env vars)
|
||||
|
||||
You'll need a wildcard TLS certificate for `*.your-pds-hostname.example.com`. User handles are served as subdomains.
|
||||
|
||||
The container image expects:
|
||||
- A TOML config file mounted at `/etc/tranquil-pds/config.toml` (or passed via `--config`)
|
||||
- `DATABASE_URL` - postgres connection string
|
||||
- `BLOB_STORAGE_PATH` - path to blob storage (mount a PV here)
|
||||
- `PDS_HOSTNAME` - your PDS hostname (without protocol)
|
||||
- `JWT_SECRET`, `DPOP_SECRET`, `MASTER_KEY` - generate with `openssl rand -base64 48`
|
||||
- `CRAWLERS` - typically `https://bsky.network`
|
||||
|
||||
and more, check the example.toml for all options. Environment variables can override any TOML value.
|
||||
You can also point to a config file via the `TRANQUIL_PDS_CONFIG` env var.
|
||||
|
||||
Health check: `GET /xrpc/_health`
|
||||
|
||||
## Custom homepage
|
||||
|
||||
Mount a ConfigMap with your `homepage.html` into the container's frontend directory and it becomes your landing page. Go nuts with it. Account dashboard is at `/app/` so you won't break anything.
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: pds-homepage
|
||||
data:
|
||||
homepage.html: |
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Welcome to my PDS</title></head>
|
||||
<body>
|
||||
<h1>Welcome to my little evil secret lab!!!</h1>
|
||||
<p><a href="/app/">Sign in</a></p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
@@ -81,14 +81,7 @@ See [example.toml](https://tangled.org/tranquil.farm/tranquil-pds/blob/main/exam
|
||||
|
||||
### Secrets
|
||||
|
||||
Additionally, you will need to provide the following environment variables.
|
||||
|
||||
```env
|
||||
# use `openssl rand -base64 32` to generate these.
|
||||
JWT_SECRET=<secret_here>
|
||||
DPOP_SECRET=<secret_here>
|
||||
MASTER_KEY=<secret_here>
|
||||
```
|
||||
Secrets must not live in the nix store. Provide the `jwt_secret`, `dpop_secret`, and `master_key` values through `environmentFiles` instead of `settings`. example.toml documents the matching environment variable name for each. Generate each with `openssl rand -base64 48`.
|
||||
|
||||
The simplest (least secure and least reproducible) option is to provide these secrets in a `.env` file using the `environmentFiles` option as shown above.
|
||||
|
||||
@@ -224,7 +217,7 @@ The log entry shold look something like this:
|
||||
|
||||
## Binary cache
|
||||
|
||||
Pre-built artifacts from the flake — the package, frontend, and devshell — are published to [tranquil.cachix.org](https://tranquil.cachix.org). To pull from it instead of building locally, add to your NixOS config:
|
||||
The flake publishes its package, frontend, and devshell to [tranquil.cachix.org](https://tranquil.cachix.org). To pull from it instead of building locally, add to your NixOS config:
|
||||
|
||||
```nix
|
||||
nix.settings = {
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# Tranquil PDS deployment using its own embedded DB
|
||||
|
||||
Welcome, brave one.
|
||||
So you're interested in leaving relational databases behind? Raw performance? Or... perhaps you simply want to run less services on your machine?
|
||||
|
||||
Tranquil's embedded DB is experimental.
|
||||
Risk of total data loss.
|
||||
|
||||
## What's the difference?
|
||||
|
||||
tranquil-store replaces the entire repository layer. When it is selected the server opens no postgres connection at all. The postgres service, its password secret, and the `database.url` value are all unused. Blob storage is however untouched: filesystem or S3 applies exactly as in the base guide.
|
||||
|
||||
2 settings select and place the store:
|
||||
|
||||
- `repo_backend` under `[storage]`, environment variable `REPO_BACKEND`. Set it to `"tranquil-store"`. The default is `"postgres"`.
|
||||
- `data_dir` under `[tranquil_store]`, environment variable `TRANQUIL_STORE_DATA_DIR`. This is optional. It defaults to `/var/lib/tranquil-pds/store`.
|
||||
|
||||
So the minimum config delta is one line:
|
||||
|
||||
```toml
|
||||
[storage]
|
||||
repo_backend = "tranquil-store"
|
||||
```
|
||||
|
||||
## That being said, here are the facts:
|
||||
|
||||
- At time of writing, there's no way to transfer an existing Tranquil instance from PG-backed to embedded or vice-versa. If you have an instance and you want to move to embedded, you'll have to spin it up as a new instance and migrate as you would normally.
|
||||
- You will absolutely want to take backups of all users' CAR files daily of not more frequently. As usual, you *really* should have rotation keys separately stored aside somewhere in case the DB explodes in an unrecoverable way.
|
||||
|
||||
## Installing: a patch on the existing guides
|
||||
|
||||
The procedure is the one in [2_INSTALL_CONTAINERS.md](2_INSTALL_CONTAINERS.md) or [2_INSTALL_NIX.md](2_INSTALL_NIX.md). Follow your chosen guide top to bottom and apply the deltas below, otherwise exactly the same!
|
||||
|
||||
### Containers
|
||||
|
||||
Both base guides assume postgres of course, and the units couple the app to it. Dropping the database means uncoupling that out too.
|
||||
|
||||
Shared, regardless of init system:
|
||||
|
||||
1. In `config.toml`, leave `database.url` unset and add the `[storage]` block shown above.
|
||||
2. Skip the database secret. No need to create `tranquil-pds-db-password`.
|
||||
3. The app unit already mounts the `store` directory, so `data_dir` needs no extra setup. The `postgres` directory in the guide's `mkdir` goes unused.
|
||||
4. Backup section: `pg_dump` does not apply. Back up the `data_dir` instead, which holds the metastore, eventlog, and blockstore. CAR files and rotation keys still belong in your own backup as mentioned above.
|
||||
|
||||
**Debian (quadlets):** Do not copy `tranquil-pds-db.container`. Drop `tranquil-pds-db` from the `systemctl start` and `systemctl enable` commands. The `After=tranquil-pds-db.service` line in `tranquil-pds-app.container` becomes a no-op with the database gone. Remove it if you want.
|
||||
|
||||
**Alpine (OpenRC):** Do not copy the `tranquil-pds-db` init script. The app script hard-depends on it via `need tranquil-pds-db`, so edit `tranquil-pds-app`'s `depend()` to read `need tranquil-pds-pod` instead. Without this the app needs a service that no longer exists and refuses to start. Drop `tranquil-pds-db` from the `rc-update add` command too.
|
||||
|
||||
### Nix
|
||||
|
||||
1. Set `services.tranquil-pds.database.createLocally = false`. This removes the local postgres service and the automatic `database.url`.
|
||||
2. Set `services.tranquil-pds.settings.storage.repo_backend = "tranquil-store";`.
|
||||
3. Leave `data_dir` at its default. It sits under the service state directory and needs no extra work. If you relocate it, ensure the service user can write there.
|
||||
|
||||
That's it!!
|
||||
|
||||
Please report anything wrong to us immediately, so that we can make our DB better, faster, stronger!
|
||||
@@ -1,12 +0,0 @@
|
||||
FROM node:24-alpine AS builder
|
||||
RUN corepack enable && corepack prepare pnpm@latest --activate
|
||||
WORKDIR /app
|
||||
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||
RUN pnpm install --frozen-lockfile
|
||||
COPY . ./
|
||||
RUN pnpm build
|
||||
|
||||
FROM nginx:1.29-alpine
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
EXPOSE 80
|
||||
@@ -1,38 +0,0 @@
|
||||
server {
|
||||
listen 8080;
|
||||
listen [::]:8080;
|
||||
server_name _;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
|
||||
|
||||
location = /oauth-client-metadata.json {
|
||||
default_type application/json;
|
||||
sub_filter_once off;
|
||||
sub_filter_types application/json;
|
||||
sub_filter '__FRONTEND_HOSTNAME__' $host;
|
||||
try_files /oauth-client-metadata.json =404;
|
||||
}
|
||||
|
||||
location /assets/ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location = / {
|
||||
try_files /homepage.html /index.html;
|
||||
}
|
||||
|
||||
location /app/ {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name _;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
|
||||
|
||||
location = /oauth-client-metadata.json {
|
||||
default_type application/json;
|
||||
sub_filter_once off;
|
||||
sub_filter_types application/json;
|
||||
sub_filter '__FRONTEND_HOSTNAME__' $host;
|
||||
try_files /oauth-client-metadata.json =404;
|
||||
}
|
||||
|
||||
location /assets/ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location = / {
|
||||
try_files /homepage.html /index.html;
|
||||
}
|
||||
|
||||
location /app/ {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
@@ -159,8 +159,9 @@ podman-down:
|
||||
podman-logs:
|
||||
podman compose logs -f
|
||||
container-build:
|
||||
podman build -t tranquil-pds:latest .
|
||||
podman build -t tranquil-pds-frontend:latest ./frontend
|
||||
podman build -t atcr.io/tranquil.farm/tranquil-pds:latest .
|
||||
container-pull:
|
||||
podman pull atcr.io/tranquil.farm/tranquil-pds:latest
|
||||
|
||||
frontend-dev:
|
||||
. ~/.deno/env && cd frontend && deno task dev
|
||||
|
||||
Reference in New Issue
Block a user