mirror of
https://tangled.org/tranquil.farm/tranquil-pds
synced 2026-08-16 22:36:07 +00:00
refactor(deploy): container-first cleanup, drop debian malware-style install
Lewis: May this revision serve well! <lu5a@proton.me>
This commit is contained in:
@@ -50,7 +50,6 @@ RUN apk add --no-cache msmtp ca-certificates \
|
||||
&& ln -sf /usr/bin/msmtp /usr/sbin/sendmail
|
||||
COPY --from=builder /tmp/tranquil-pds /usr/local/bin/tranquil-pds
|
||||
COPY --from=frontend /app/dist /var/lib/tranquil-pds/frontend
|
||||
COPY migrations /app/migrations
|
||||
WORKDIR /app
|
||||
ENV SERVER_HOST=0.0.0.0
|
||||
ENV SERVER_PORT=3000
|
||||
|
||||
@@ -62,7 +62,6 @@ podman-compose -f docker-compose.prod.yaml up -d
|
||||
### Installation Guides
|
||||
|
||||
- [Nix](docs/install-nix.md)
|
||||
- [Debian](docs/install-debian.md)
|
||||
- [Containers](docs/install-containers.md)
|
||||
- [Kubernetes](docs/install-kubernetes.md)
|
||||
|
||||
|
||||
@@ -83,8 +83,9 @@ pub fn ensure_test_defaults() {
|
||||
///
|
||||
/// Precedence (highest to lowest):
|
||||
/// 1. Environment variables
|
||||
/// 2. TOML config file (if provided)
|
||||
/// 3. Built-in defaults
|
||||
/// 2. Toml config file passed as `config_path`, if provided
|
||||
/// 3. `/etc/tranquil-pds/config.toml` - hardcoded fallback, silently skipped if absent
|
||||
/// 4. Built-in defaults
|
||||
pub fn load(config_path: Option<&PathBuf>) -> Result<TranquilConfig, confique::Error> {
|
||||
let mut builder = TranquilConfig::builder().env();
|
||||
if let Some(path) = config_path {
|
||||
|
||||
@@ -191,16 +191,12 @@ async fn setup_with_external_infra() -> String {
|
||||
async fn setup_with_testcontainers() -> String {
|
||||
let temp_dir = std::env::temp_dir().join(format!("tranquil-pds-test-{}", uuid::Uuid::new_v4()));
|
||||
let blob_path = temp_dir.join("blobs");
|
||||
let backup_path = temp_dir.join("backups");
|
||||
std::fs::create_dir_all(&blob_path).expect("Failed to create blob temp directory");
|
||||
std::fs::create_dir_all(&backup_path).expect("Failed to create backup temp directory");
|
||||
TEST_TEMP_DIR.set(temp_dir).ok();
|
||||
let plc_url = setup_mock_plc_directory().await;
|
||||
unsafe {
|
||||
std::env::set_var("BLOB_STORAGE_BACKEND", "filesystem");
|
||||
std::env::set_var("BLOB_STORAGE_PATH", blob_path.to_str().unwrap());
|
||||
std::env::set_var("BACKUP_STORAGE_BACKEND", "filesystem");
|
||||
std::env::set_var("BACKUP_STORAGE_PATH", backup_path.to_str().unwrap());
|
||||
std::env::set_var("MAX_IMPORT_SIZE", "100000000");
|
||||
std::env::set_var("SKIP_IMPORT_VERIFICATION", "true");
|
||||
std::env::set_var("PLC_DIRECTORY_URL", &plc_url);
|
||||
@@ -242,8 +238,6 @@ async fn setup_with_testcontainers() -> String {
|
||||
let plc_url = setup_mock_plc_directory().await;
|
||||
unsafe {
|
||||
std::env::set_var("BLOB_STORAGE_BACKEND", "s3");
|
||||
std::env::set_var("BACKUP_STORAGE_BACKEND", "s3");
|
||||
std::env::set_var("BACKUP_S3_BUCKET", "test-backups");
|
||||
std::env::set_var("S3_BUCKET", "test-bucket");
|
||||
std::env::set_var("AWS_ACCESS_KEY_ID", "minioadmin");
|
||||
std::env::set_var("AWS_SECRET_ACCESS_KEY", "minioadmin");
|
||||
@@ -333,8 +327,6 @@ unsafe fn configure_external_storage_env() {
|
||||
if std::env::var("S3_ENDPOINT").is_ok() {
|
||||
let s3_endpoint = std::env::var("S3_ENDPOINT").unwrap();
|
||||
std::env::set_var("BLOB_STORAGE_BACKEND", "s3");
|
||||
std::env::set_var("BACKUP_STORAGE_BACKEND", "s3");
|
||||
std::env::set_var("BACKUP_S3_BUCKET", "test-backups");
|
||||
std::env::set_var(
|
||||
"S3_BUCKET",
|
||||
std::env::var("S3_BUCKET").unwrap_or_else(|_| "test-bucket".to_string()),
|
||||
@@ -356,14 +348,10 @@ unsafe fn configure_external_storage_env() {
|
||||
let process_dir =
|
||||
std::env::temp_dir().join(format!("tranquil-pds-test-{}", std::process::id()));
|
||||
let blob_path = process_dir.join("blobs");
|
||||
let backup_path = process_dir.join("backups");
|
||||
std::fs::create_dir_all(&blob_path).expect("Failed to create blob directory");
|
||||
std::fs::create_dir_all(&backup_path).expect("Failed to create backup directory");
|
||||
TEST_TEMP_DIR.set(process_dir).ok();
|
||||
std::env::set_var("BLOB_STORAGE_BACKEND", "filesystem");
|
||||
std::env::set_var("BLOB_STORAGE_PATH", blob_path.to_str().unwrap());
|
||||
std::env::set_var("BACKUP_STORAGE_BACKEND", "filesystem");
|
||||
std::env::set_var("BACKUP_STORAGE_PATH", backup_path.to_str().unwrap());
|
||||
}
|
||||
std::env::set_var("MAX_IMPORT_SIZE", "100000000");
|
||||
std::env::set_var("SKIP_IMPORT_VERIFICATION", "true");
|
||||
@@ -622,18 +610,14 @@ async fn setup_store_backend() -> String {
|
||||
let temp_dir =
|
||||
std::env::temp_dir().join(format!("tranquil-pds-store-{}", uuid::Uuid::new_v4()));
|
||||
let blob_path = temp_dir.join("blobs");
|
||||
let backup_path = temp_dir.join("backups");
|
||||
let store_path = temp_dir.join("store");
|
||||
std::fs::create_dir_all(&blob_path).expect("failed to create blob temp directory");
|
||||
std::fs::create_dir_all(&backup_path).expect("failed to create backup temp directory");
|
||||
std::fs::create_dir_all(&store_path).expect("failed to create store temp directory");
|
||||
TEST_TEMP_DIR.set(temp_dir).ok();
|
||||
let plc_url = setup_mock_plc_directory().await;
|
||||
unsafe {
|
||||
std::env::set_var("BLOB_STORAGE_BACKEND", "filesystem");
|
||||
std::env::set_var("BLOB_STORAGE_PATH", blob_path.to_str().unwrap());
|
||||
std::env::set_var("BACKUP_STORAGE_BACKEND", "filesystem");
|
||||
std::env::set_var("BACKUP_STORAGE_PATH", backup_path.to_str().unwrap());
|
||||
std::env::set_var("MAX_IMPORT_SIZE", "100000000");
|
||||
std::env::set_var("SKIP_IMPORT_VERIFICATION", "true");
|
||||
std::env::set_var("PLC_DIRECTORY_URL", &plc_url);
|
||||
@@ -790,18 +774,14 @@ async fn setup_cluster_store_backend() -> Option<sqlx::PgPool> {
|
||||
uuid::Uuid::new_v4()
|
||||
));
|
||||
let blob_path = temp_dir.join("blobs");
|
||||
let backup_path = temp_dir.join("backups");
|
||||
let store_path = temp_dir.join("store");
|
||||
std::fs::create_dir_all(&blob_path).expect("failed to create blob temp directory");
|
||||
std::fs::create_dir_all(&backup_path).expect("failed to create backup temp directory");
|
||||
std::fs::create_dir_all(&store_path).expect("failed to create store temp directory");
|
||||
TEST_TEMP_DIR.set(temp_dir).ok();
|
||||
let plc_url = setup_mock_plc_directory().await;
|
||||
unsafe {
|
||||
std::env::set_var("BLOB_STORAGE_BACKEND", "filesystem");
|
||||
std::env::set_var("BLOB_STORAGE_PATH", blob_path.to_str().unwrap());
|
||||
std::env::set_var("BACKUP_STORAGE_BACKEND", "filesystem");
|
||||
std::env::set_var("BACKUP_STORAGE_PATH", backup_path.to_str().unwrap());
|
||||
std::env::set_var("MAX_IMPORT_SIZE", "100000000");
|
||||
std::env::set_var("SKIP_IMPORT_VERIFICATION", "true");
|
||||
std::env::set_var("PLC_DIRECTORY_URL", &plc_url);
|
||||
@@ -847,16 +827,12 @@ async fn setup_cluster_testcontainers() -> Option<sqlx::PgPool> {
|
||||
let temp_dir =
|
||||
std::env::temp_dir().join(format!("tranquil-pds-cluster-{}", uuid::Uuid::new_v4()));
|
||||
let blob_path = temp_dir.join("blobs");
|
||||
let backup_path = temp_dir.join("backups");
|
||||
std::fs::create_dir_all(&blob_path).expect("Failed to create blob temp directory");
|
||||
std::fs::create_dir_all(&backup_path).expect("Failed to create backup temp directory");
|
||||
TEST_TEMP_DIR.set(temp_dir).ok();
|
||||
let plc_url = setup_mock_plc_directory().await;
|
||||
unsafe {
|
||||
std::env::set_var("BLOB_STORAGE_BACKEND", "filesystem");
|
||||
std::env::set_var("BLOB_STORAGE_PATH", blob_path.to_str().unwrap());
|
||||
std::env::set_var("BACKUP_STORAGE_BACKEND", "filesystem");
|
||||
std::env::set_var("BACKUP_STORAGE_PATH", backup_path.to_str().unwrap());
|
||||
std::env::set_var("MAX_IMPORT_SIZE", "100000000");
|
||||
std::env::set_var("SKIP_IMPORT_VERIFICATION", "true");
|
||||
std::env::set_var("PLC_DIRECTORY_URL", &plc_url);
|
||||
|
||||
@@ -8,7 +8,6 @@ const HANDLE_DOMAIN: &str = "handles.test";
|
||||
|
||||
fn set_handle_domain() {
|
||||
unsafe {
|
||||
std::env::set_var("AVAILABLE_USER_DOMAINS", HANDLE_DOMAIN);
|
||||
std::env::set_var("PDS_USER_HANDLE_DOMAINS", HANDLE_DOMAIN);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ After=tranquil-pds-db.service
|
||||
ContainerName=tranquil-pds-app
|
||||
Image=localhost/tranquil-pds:latest
|
||||
Pod=tranquil-pds.pod
|
||||
EnvironmentFile=/srv/tranquil-pds/config/tranquil-pds.env
|
||||
Environment=SERVER_HOST=0.0.0.0
|
||||
Environment=SERVER_PORT=3000
|
||||
Volume=/srv/tranquil-pds/blobs:/var/lib/tranquil/blobs:Z
|
||||
Volume=/srv/tranquil-pds/backups:/var/lib/tranquil/backups:Z
|
||||
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
|
||||
Volume=/srv/tranquil-pds/store:/var/lib/tranquil-pds/store:Z
|
||||
HealthCmd=wget -q --spider http://localhost:3000/xrpc/_health
|
||||
HealthInterval=30s
|
||||
HealthTimeout=10s
|
||||
|
||||
@@ -9,7 +9,9 @@ services:
|
||||
SERVER_HOST: "0.0.0.0"
|
||||
volumes:
|
||||
- ./config.toml:/etc/tranquil-pds/config.toml:ro
|
||||
- blob_data:/var/lib/tranquil/blobs
|
||||
# In memory of @olaren.dev's blobs when lewis forgot to update /tranquil to /tranquil-pds :(
|
||||
- blob_data:/var/lib/tranquil-pds/blobs
|
||||
- store_data:/var/lib/tranquil-pds/store
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
@@ -94,5 +96,6 @@ services:
|
||||
volumes:
|
||||
postgres_data:
|
||||
blob_data:
|
||||
store_data:
|
||||
prometheus_data:
|
||||
acme_challenge:
|
||||
|
||||
+3
-1
@@ -10,7 +10,8 @@ services:
|
||||
DATABASE_URL: postgres://postgres:postgres@db:5432/pds
|
||||
volumes:
|
||||
- ./config.toml:/etc/tranquil-pds/config.toml:ro
|
||||
- blob_data:/var/lib/tranquil/blobs
|
||||
- blob_data:/var/lib/tranquil-pds/blobs
|
||||
- store_data:/var/lib/tranquil-pds/store
|
||||
depends_on:
|
||||
- db
|
||||
|
||||
@@ -51,4 +52,5 @@ services:
|
||||
volumes:
|
||||
postgres_data:
|
||||
blob_data:
|
||||
store_data:
|
||||
prometheus_data:
|
||||
|
||||
@@ -47,7 +47,7 @@ For production setups with proper service management, continue to either the Deb
|
||||
|
||||
## Standalone containers (no compose)
|
||||
|
||||
If you already have postgres running on the host (eg. from the [Debian install guide](install-debian.md)), you can run just the app containers.
|
||||
If you already have postgres running on the host, you can run just the app containers.
|
||||
|
||||
Build the images:
|
||||
```sh
|
||||
@@ -60,7 +60,7 @@ Run the backend with host networking (so it can access postgres on localhost) an
|
||||
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:/var/lib/tranquil:Z \
|
||||
-v /var/lib/tranquil-pds:/var/lib/tranquil-pds:Z \
|
||||
tranquil-pds:latest
|
||||
```
|
||||
|
||||
@@ -91,7 +91,7 @@ location / {
|
||||
}
|
||||
```
|
||||
|
||||
See the [Debian install guide](install-debian.md) for the full nginx config with all API routes.
|
||||
See the Debian with systemd quadlets section below for the full nginx config with all API routes.
|
||||
|
||||
---
|
||||
|
||||
@@ -110,7 +110,7 @@ apt install -y podman
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/containers/systemd
|
||||
mkdir -p /srv/tranquil-pds/{postgres,blobs,certs,acme,config}
|
||||
mkdir -p /srv/tranquil-pds/{postgres,blobs,store,certs,acme,config}
|
||||
```
|
||||
|
||||
## Create a configuration file
|
||||
|
||||
@@ -1,370 +0,0 @@
|
||||
# Tranquil PDS production installation on debian
|
||||
|
||||
This guide covers installing Tranquil PDS on Debian.
|
||||
|
||||
It is a "compile the thing on the server itself" -style guide.
|
||||
This cop-out is because Tranquil isn't built and released via CI as of yet.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A server :p
|
||||
- Disk space enough for blobs (depends on usage; plan for ~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)
|
||||
- Root/sudo/doas access
|
||||
|
||||
## System setup
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
apt install -y curl git build-essential pkg-config libssl-dev
|
||||
```
|
||||
|
||||
## Install rust
|
||||
|
||||
```bash
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
source ~/.cargo/env
|
||||
rustup default stable
|
||||
```
|
||||
|
||||
This installs the latest stable Rust.
|
||||
|
||||
## Install postgres
|
||||
|
||||
```bash
|
||||
apt install -y postgresql postgresql-contrib
|
||||
systemctl enable postgresql
|
||||
systemctl start postgresql
|
||||
sudo -u postgres psql -c "CREATE USER tranquil_pds WITH PASSWORD 'your-secure-password';"
|
||||
sudo -u postgres psql -c "CREATE DATABASE pds OWNER tranquil_pds;"
|
||||
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE pds TO tranquil_pds;"
|
||||
```
|
||||
|
||||
## Create blob storage directories
|
||||
|
||||
```bash
|
||||
mkdir -p /var/lib/tranquil/blobs
|
||||
```
|
||||
|
||||
We'll set ownership after creating the service user.
|
||||
|
||||
## Install Node.js and pnpm (for frontend build)
|
||||
|
||||
```bash
|
||||
curl -fsSL https://deb.nodesource.com/setup_24.x | bash -
|
||||
apt install -y nodejs
|
||||
npm install -g pnpm
|
||||
```
|
||||
|
||||
## Clone and build Tranquil PDS
|
||||
|
||||
```bash
|
||||
cd /opt
|
||||
git clone https://tangled.org/tranquil.farm/tranquil-pds tranquil-pds
|
||||
cd tranquil-pds
|
||||
cd frontend
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm build
|
||||
cd ..
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
## Configure Tranquil PDS
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/tranquil-pds
|
||||
cp /opt/tranquil-pds/example.toml /etc/tranquil-pds/config.toml
|
||||
chmod 600 /etc/tranquil-pds/config.toml
|
||||
```
|
||||
|
||||
Edit `/etc/tranquil-pds/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. You can also pass the config file path
|
||||
> via the `TRANQUIL_PDS_CONFIG` env var instead of `--config`.
|
||||
|
||||
You can validate your configuration before starting the service:
|
||||
```bash
|
||||
/usr/local/bin/tranquil-pds --config /etc/tranquil-pds/config.toml validate
|
||||
```
|
||||
|
||||
## Install frontend files
|
||||
|
||||
```bash
|
||||
mkdir -p /var/www/tranquil-pds
|
||||
cp -r /opt/tranquil-pds/frontend/dist/* /var/www/tranquil-pds/
|
||||
chown -R www-data:www-data /var/www/tranquil-pds
|
||||
```
|
||||
|
||||
## Create systemd service
|
||||
|
||||
```bash
|
||||
useradd -r -s /sbin/nologin tranquil-pds
|
||||
chown -R tranquil-pds:tranquil-pds /var/lib/tranquil
|
||||
cp /opt/tranquil-pds/target/release/tranquil-pds /usr/local/bin/
|
||||
|
||||
cat > /etc/systemd/system/tranquil-pds.service << 'EOF'
|
||||
[Unit]
|
||||
Description=Tranquil PDS - AT Protocol PDS
|
||||
After=network.target postgresql.service
|
||||
[Service]
|
||||
Type=simple
|
||||
User=tranquil-pds
|
||||
Group=tranquil-pds
|
||||
ExecStart=/usr/local/bin/tranquil-pds --config /etc/tranquil-pds/config.toml
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
PrivateTmp=true
|
||||
ReadWritePaths=/var/lib/tranquil
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable tranquil-pds
|
||||
systemctl start tranquil-pds
|
||||
```
|
||||
|
||||
## Install and configure nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx certbot python3-certbot-nginx
|
||||
|
||||
cat > /etc/nginx/sites-available/tranquil-pds << 'EOF'
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name pds.example.com *.pds.example.com;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/acme;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
http2 on;
|
||||
server_name pds.example.com *.pds.example.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/pds.example.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/pds.example.com/privkey.pem;
|
||||
|
||||
client_max_body_size 10G;
|
||||
|
||||
root /var/www/tranquil-pds;
|
||||
|
||||
location /xrpc/ {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
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_read_timeout 86400;
|
||||
proxy_send_timeout 86400;
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
}
|
||||
|
||||
location = /oauth-client-metadata.json {
|
||||
root /var/www/tranquil-pds;
|
||||
default_type application/json;
|
||||
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_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_read_timeout 300;
|
||||
proxy_send_timeout 300;
|
||||
}
|
||||
|
||||
location /.well-known/ {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
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;
|
||||
}
|
||||
|
||||
location /webhook/ {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
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;
|
||||
}
|
||||
|
||||
location = /metrics {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
location = /health {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
location = /robots.txt {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
location = /logo {
|
||||
proxy_pass http://127.0.0.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_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;
|
||||
}
|
||||
|
||||
location /assets/ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location /app/ {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location = / {
|
||||
try_files /homepage.html /index.html;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
ln -sf /etc/nginx/sites-available/tranquil-pds /etc/nginx/sites-enabled/
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
mkdir -p /var/www/acme
|
||||
nginx -t
|
||||
systemctl reload nginx
|
||||
```
|
||||
|
||||
## 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. If your DNS provider has a certbot plugin:
|
||||
```bash
|
||||
apt install -y python3-certbot-dns-cloudflare
|
||||
certbot certonly --dns-cloudflare \
|
||||
--dns-cloudflare-credentials /etc/cloudflare.ini \
|
||||
-d pds.example.com -d '*.pds.example.com'
|
||||
```
|
||||
|
||||
For manual DNS validation (works with any provider):
|
||||
```bash
|
||||
certbot certonly --manual --preferred-challenges dns \
|
||||
-d pds.example.com -d '*.pds.example.com'
|
||||
```
|
||||
|
||||
Follow the prompts to add TXT records to your DNS. Note: manual mode doesn't auto-renew.
|
||||
|
||||
After obtaining the cert, reload nginx:
|
||||
```bash
|
||||
systemctl reload nginx
|
||||
```
|
||||
|
||||
## Configure firewall if you're into that sort of thing
|
||||
|
||||
```bash
|
||||
apt install -y ufw
|
||||
ufw allow ssh
|
||||
ufw allow 80/tcp
|
||||
ufw allow 443/tcp
|
||||
ufw enable
|
||||
```
|
||||
|
||||
## Verify installation
|
||||
|
||||
```bash
|
||||
systemctl status tranquil-pds
|
||||
curl -s https://pds.example.com/xrpc/_health | jq
|
||||
curl -s https://pds.example.com/.well-known/atproto-did
|
||||
```
|
||||
|
||||
## Maintenance
|
||||
|
||||
View logs:
|
||||
```bash
|
||||
journalctl -u tranquil-pds -f
|
||||
```
|
||||
|
||||
Update Tranquil PDS:
|
||||
```bash
|
||||
cd /opt/tranquil-pds
|
||||
git pull
|
||||
cd frontend && pnpm install --frozen-lockfile && pnpm build && cd ..
|
||||
cargo build --release
|
||||
systemctl stop tranquil-pds
|
||||
cp target/release/tranquil-pds /usr/local/bin/
|
||||
cp -r frontend/dist/* /var/www/tranquil-pds/
|
||||
systemctl start tranquil-pds
|
||||
```
|
||||
|
||||
Tranquil should auto-migrate if there are any new migrations to be applied to the db, so you don't need to worry.
|
||||
|
||||
Backup database:
|
||||
```bash
|
||||
sudo -u postgres pg_dump pds > /var/backups/pds-$(date +%Y%m%d).sql
|
||||
```
|
||||
|
||||
## Custom homepage
|
||||
|
||||
Drop a `homepage.html` in `/var/www/tranquil-pds/` and it becomes your landing page. Account dashboard is at `/app/` so you won't break anything.
|
||||
|
||||
```bash
|
||||
cat > /var/www/tranquil-pds/homepage.html << 'EOF'
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Welcome to my PDS</title>
|
||||
<style>
|
||||
body { font-family: system-ui; max-width: 600px; margin: 100px auto; padding: 20px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Welcome to my secret PDS</h1>
|
||||
<p>This is a <a href="https://atproto.com">AT Protocol</a> Personal Data Server.</p>
|
||||
<p><a href="/app/">Sign in</a> or learn more at <a href="https://bsky.social">Bluesky</a>.</p>
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
```
|
||||
@@ -1,512 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[OK]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
log_error "This script must be run as root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -qi "debian" /etc/os-release 2>/dev/null; then
|
||||
log_warn "This script is designed for Debian. Proceed with caution on other distros."
|
||||
fi
|
||||
|
||||
nuke_installation() {
|
||||
log_warn "NUKING EXISTING INSTALLATION"
|
||||
log_info "Stopping services..."
|
||||
systemctl stop tranquil-pds 2>/dev/null || true
|
||||
systemctl disable tranquil-pds 2>/dev/null || true
|
||||
|
||||
log_info "Removing Tranquil PDS files..."
|
||||
rm -rf /opt/tranquil-pds
|
||||
rm -rf /var/lib/tranquil-pds
|
||||
rm -f /usr/local/bin/tranquil-pds
|
||||
rm -f /usr/local/bin/tranquil-pds-sendmail
|
||||
rm -f /usr/local/bin/tranquil-pds-mailq
|
||||
rm -rf /var/spool/tranquil-pds-mail
|
||||
rm -f /etc/systemd/system/tranquil-pds.service
|
||||
systemctl daemon-reload
|
||||
|
||||
log_info "Removing Tranquil PDS configuration..."
|
||||
rm -rf /etc/tranquil-pds
|
||||
|
||||
log_info "Dropping postgres database and user..."
|
||||
sudo -u postgres psql -c "DROP DATABASE IF EXISTS pds;" 2>/dev/null || true
|
||||
sudo -u postgres psql -c "DROP USER IF EXISTS tranquil_pds;" 2>/dev/null || true
|
||||
|
||||
log_info "Removing blob storage..."
|
||||
rm -rf /var/lib/tranquil 2>/dev/null || true
|
||||
|
||||
log_info "Removing nginx config..."
|
||||
rm -f /etc/nginx/sites-enabled/tranquil-pds
|
||||
rm -f /etc/nginx/sites-available/tranquil-pds
|
||||
systemctl reload nginx 2>/dev/null || true
|
||||
|
||||
log_success "Previous installation nuked"
|
||||
}
|
||||
|
||||
if [[ -f /etc/tranquil-pds/tranquil-pds.env ]] || [[ -d /opt/tranquil-pds ]] || [[ -f /usr/local/bin/tranquil-pds ]]; then
|
||||
log_warn "Existing installation detected"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " 1) Nuke everything and start fresh (destroys database!)"
|
||||
echo " 2) Continue with existing installation (idempotent update)"
|
||||
echo " 3) Exit"
|
||||
echo ""
|
||||
read -p "Choose an option [1/2/3]: " INSTALL_CHOICE
|
||||
|
||||
case "$INSTALL_CHOICE" in
|
||||
1)
|
||||
echo ""
|
||||
log_warn "This will DELETE:"
|
||||
echo " - PostgreSQL database 'pds' and all data"
|
||||
echo " - All Tranquil PDS configuration and credentials"
|
||||
echo " - All source code in /opt/tranquil-pds"
|
||||
echo " - All blobs in /var/lib/tranquil/"
|
||||
echo ""
|
||||
read -p "Type 'NUKE' to confirm: " CONFIRM_NUKE
|
||||
if [[ "$CONFIRM_NUKE" == "NUKE" ]]; then
|
||||
nuke_installation
|
||||
else
|
||||
log_error "Nuke cancelled"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
2)
|
||||
log_info "Continuing with existing installation..."
|
||||
;;
|
||||
3)
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log_error "Invalid option"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_info "Tranquil PDS Installation Script for Debian"
|
||||
echo ""
|
||||
|
||||
get_public_ips() {
|
||||
IPV4=$(curl -4 -s --max-time 5 ifconfig.me 2>/dev/null || curl -4 -s --max-time 5 icanhazip.com 2>/dev/null || echo "Could not detect")
|
||||
IPV6=$(curl -6 -s --max-time 5 ifconfig.me 2>/dev/null || curl -6 -s --max-time 5 icanhazip.com 2>/dev/null || echo "")
|
||||
}
|
||||
|
||||
log_info "Detecting public IP addresses..."
|
||||
get_public_ips
|
||||
echo " IPv4: ${IPV4}"
|
||||
[[ -n "$IPV6" ]] && echo " IPv6: ${IPV6}"
|
||||
echo ""
|
||||
|
||||
read -p "Enter your PDS domain (eg., pds.example.com): " PDS_DOMAIN
|
||||
if [[ -z "$PDS_DOMAIN" ]]; then
|
||||
log_error "Domain cannot be empty"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
read -p "Enter your email for Let's Encrypt: " CERTBOT_EMAIL
|
||||
if [[ -z "$CERTBOT_EMAIL" ]]; then
|
||||
log_error "Email cannot be empty"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_info "DNS records required (create these now if you haven't):"
|
||||
echo ""
|
||||
echo " ${PDS_DOMAIN} A ${IPV4}"
|
||||
[[ -n "$IPV6" ]] && echo " ${PDS_DOMAIN} AAAA ${IPV6}"
|
||||
echo " *.${PDS_DOMAIN} A ${IPV4} (for user handles)"
|
||||
[[ -n "$IPV6" ]] && echo " *.${PDS_DOMAIN} AAAA ${IPV6} (for user handles)"
|
||||
echo ""
|
||||
read -p "Have you created these DNS records? (y/N): " DNS_CONFIRMED
|
||||
if [[ ! "$DNS_CONFIRMED" =~ ^[Yy]$ ]]; then
|
||||
log_warn "Please create the DNS records and run this script again."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
CREDENTIALS_FILE="/etc/tranquil-pds/.credentials"
|
||||
if [[ -f "$CREDENTIALS_FILE" ]]; then
|
||||
log_info "Loading existing credentials..."
|
||||
source "$CREDENTIALS_FILE"
|
||||
else
|
||||
log_info "Generating secrets..."
|
||||
JWT_SECRET=$(openssl rand -base64 48)
|
||||
DPOP_SECRET=$(openssl rand -base64 48)
|
||||
MASTER_KEY=$(openssl rand -base64 48)
|
||||
DB_PASSWORD=$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 32)
|
||||
|
||||
mkdir -p /etc/tranquil-pds
|
||||
cat > "$CREDENTIALS_FILE" << EOF
|
||||
JWT_SECRET="$JWT_SECRET"
|
||||
DPOP_SECRET="$DPOP_SECRET"
|
||||
MASTER_KEY="$MASTER_KEY"
|
||||
DB_PASSWORD="$DB_PASSWORD"
|
||||
EOF
|
||||
chmod 600 "$CREDENTIALS_FILE"
|
||||
log_success "Secrets generated"
|
||||
fi
|
||||
|
||||
log_info "Checking swap space..."
|
||||
TOTAL_MEM_KB=$(grep MemTotal /proc/meminfo | awk '{print $2}')
|
||||
TOTAL_SWAP_KB=$(grep SwapTotal /proc/meminfo | awk '{print $2}')
|
||||
|
||||
if [[ $TOTAL_SWAP_KB -lt 2000000 ]]; then
|
||||
if [[ ! -f /swapfile ]]; then
|
||||
log_info "Adding swap space for compilation..."
|
||||
SWAP_SIZE="4G"
|
||||
[[ $TOTAL_MEM_KB -ge 4000000 ]] && SWAP_SIZE="2G"
|
||||
fallocate -l $SWAP_SIZE /swapfile || dd if=/dev/zero of=/swapfile bs=1M count=4096
|
||||
chmod 600 /swapfile
|
||||
mkswap /swapfile
|
||||
swapon /swapfile
|
||||
grep -q '/swapfile' /etc/fstab || echo '/swapfile none swap sw 0 0' >> /etc/fstab
|
||||
log_success "Swap added ($SWAP_SIZE)"
|
||||
else
|
||||
swapon /swapfile 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
log_info "Updating system packages..."
|
||||
apt update && apt upgrade -y
|
||||
|
||||
log_info "Installing build dependencies..."
|
||||
apt install -y curl git build-essential pkg-config libssl-dev ca-certificates gnupg lsb-release unzip xxd
|
||||
|
||||
log_info "Installing postgres..."
|
||||
apt install -y postgresql postgresql-contrib
|
||||
systemctl enable postgresql
|
||||
systemctl start postgresql
|
||||
sudo -u postgres psql -c "CREATE USER tranquil_pds WITH PASSWORD '${DB_PASSWORD}';" 2>/dev/null || \
|
||||
sudo -u postgres psql -c "ALTER USER tranquil_pds WITH PASSWORD '${DB_PASSWORD}';"
|
||||
sudo -u postgres psql -c "CREATE DATABASE pds OWNER tranquil_pds;" 2>/dev/null || true
|
||||
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE pds TO tranquil_pds;"
|
||||
log_success "postgres configured"
|
||||
|
||||
log_info "Creating blob storage directories..."
|
||||
mkdir -p /var/lib/tranquil/blobs
|
||||
log_success "Blob storage directories created"
|
||||
|
||||
log_info "Installing rust..."
|
||||
if [[ -f "$HOME/.cargo/env" ]]; then
|
||||
source "$HOME/.cargo/env"
|
||||
fi
|
||||
if ! command -v rustc &>/dev/null; then
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
source "$HOME/.cargo/env"
|
||||
fi
|
||||
|
||||
log_info "Installing Node.js..."
|
||||
if ! command -v node &>/dev/null; then
|
||||
curl -fsSL https://deb.nodesource.com/setup_24.x | bash -
|
||||
apt install -y nodejs
|
||||
fi
|
||||
|
||||
log_info "Installing pnpm..."
|
||||
if ! command -v pnpm &>/dev/null; then
|
||||
npm install -g pnpm
|
||||
fi
|
||||
|
||||
log_info "Cloning Tranquil PDS..."
|
||||
if [[ ! -d /opt/tranquil-pds ]]; then
|
||||
git clone https://tangled.org/tranquil.farm/tranquil-pds /opt/tranquil-pds
|
||||
else
|
||||
cd /opt/tranquil-pds && git pull
|
||||
fi
|
||||
cd /opt/tranquil-pds
|
||||
|
||||
log_info "Building frontend..."
|
||||
cd frontend && pnpm install --frozen-lockfile && pnpm build && cd ..
|
||||
log_success "Frontend built"
|
||||
|
||||
log_info "Building Tranquil PDS (this takes a while)..."
|
||||
source "$HOME/.cargo/env"
|
||||
if [[ $TOTAL_MEM_KB -lt 4000000 ]]; then
|
||||
log_info "Low memory - limiting parallel jobs"
|
||||
CARGO_BUILD_JOBS=1 cargo build --release
|
||||
else
|
||||
cargo build --release
|
||||
fi
|
||||
log_success "Tranquil PDS built"
|
||||
|
||||
log_info "Running migrations..."
|
||||
cargo install sqlx-cli --no-default-features --features postgres
|
||||
export DATABASE_URL="postgres://tranquil_pds:${DB_PASSWORD}@localhost:5432/pds"
|
||||
"$HOME/.cargo/bin/sqlx" migrate run
|
||||
log_success "Migrations complete"
|
||||
|
||||
log_info "Setting up mail trap..."
|
||||
mkdir -p /var/spool/tranquil-pds-mail
|
||||
chmod 1777 /var/spool/tranquil-pds-mail
|
||||
|
||||
cat > /usr/local/bin/tranquil-pds-sendmail << 'SENDMAIL_EOF'
|
||||
#!/bin/bash
|
||||
MAIL_DIR="/var/spool/tranquil-pds-mail"
|
||||
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
||||
RANDOM_ID=$(head -c 4 /dev/urandom | xxd -p)
|
||||
MAIL_FILE="${MAIL_DIR}/${TIMESTAMP}-${RANDOM_ID}.eml"
|
||||
mkdir -p "$MAIL_DIR"
|
||||
{
|
||||
echo "X-Tranquil-PDS-Received: $(date -Iseconds)"
|
||||
echo "X-Tranquil-PDS-Args: $*"
|
||||
echo ""
|
||||
cat
|
||||
} > "$MAIL_FILE"
|
||||
chmod 644 "$MAIL_FILE"
|
||||
exit 0
|
||||
SENDMAIL_EOF
|
||||
chmod +x /usr/local/bin/tranquil-pds-sendmail
|
||||
|
||||
cat > /usr/local/bin/tranquil-pds-mailq << 'MAILQ_EOF'
|
||||
#!/bin/bash
|
||||
MAIL_DIR="/var/spool/tranquil-pds-mail"
|
||||
case "${1:-list}" in
|
||||
list)
|
||||
ls -lt "$MAIL_DIR"/*.eml 2>/dev/null | head -20 || echo "No emails"
|
||||
;;
|
||||
latest)
|
||||
f=$(ls -t "$MAIL_DIR"/*.eml 2>/dev/null | head -1)
|
||||
[[ -f "$f" ]] && cat "$f" || echo "No emails"
|
||||
;;
|
||||
clear)
|
||||
rm -f "$MAIL_DIR"/*.eml
|
||||
echo "Cleared"
|
||||
;;
|
||||
count)
|
||||
ls -1 "$MAIL_DIR"/*.eml 2>/dev/null | wc -l
|
||||
;;
|
||||
[0-9]*)
|
||||
f=$(ls -t "$MAIL_DIR"/*.eml 2>/dev/null | sed -n "${1}p")
|
||||
[[ -f "$f" ]] && cat "$f" || echo "Not found"
|
||||
;;
|
||||
*)
|
||||
[[ -f "$MAIL_DIR/$1" ]] && cat "$MAIL_DIR/$1" || echo "Usage: tranquil-pds-mailq [list|latest|clear|count|N]"
|
||||
;;
|
||||
esac
|
||||
MAILQ_EOF
|
||||
chmod +x /usr/local/bin/tranquil-pds-mailq
|
||||
|
||||
log_info "Creating Tranquil PDS configuration..."
|
||||
cat > /etc/tranquil-pds/tranquil-pds.env << EOF
|
||||
SERVER_HOST=127.0.0.1
|
||||
SERVER_PORT=3000
|
||||
PDS_HOSTNAME=${PDS_DOMAIN}
|
||||
DATABASE_URL=postgres://tranquil_pds:${DB_PASSWORD}@localhost:5432/pds
|
||||
DATABASE_MAX_CONNECTIONS=100
|
||||
DATABASE_MIN_CONNECTIONS=10
|
||||
BLOB_STORAGE_PATH=/var/lib/tranquil/blobs
|
||||
JWT_SECRET=${JWT_SECRET}
|
||||
DPOP_SECRET=${DPOP_SECRET}
|
||||
MASTER_KEY=${MASTER_KEY}
|
||||
PLC_DIRECTORY_URL=https://plc.directory
|
||||
CRAWLERS=https://bsky.network
|
||||
AVAILABLE_USER_DOMAINS=${PDS_DOMAIN}
|
||||
MAIL_FROM_ADDRESS=noreply@${PDS_DOMAIN}
|
||||
MAIL_FROM_NAME=Tranquil PDS
|
||||
SENDMAIL_PATH=/usr/local/bin/tranquil-pds-sendmail
|
||||
EOF
|
||||
chmod 600 /etc/tranquil-pds/tranquil-pds.env
|
||||
|
||||
log_info "Installing Tranquil PDS..."
|
||||
id -u tranquil-pds &>/dev/null || useradd -r -s /sbin/nologin tranquil-pds
|
||||
cp /opt/tranquil-pds/target/release/tranquil-server /usr/local/bin/tranquil-pds
|
||||
mkdir -p /var/lib/tranquil-pds
|
||||
cp -r /opt/tranquil-pds/frontend/dist /var/lib/tranquil-pds/frontend
|
||||
chown -R tranquil-pds:tranquil-pds /var/lib/tranquil-pds
|
||||
chown -R tranquil-pds:tranquil-pds /var/lib/tranquil
|
||||
|
||||
cat > /etc/systemd/system/tranquil-pds.service << 'EOF'
|
||||
[Unit]
|
||||
Description=Tranquil PDS - AT Protocol PDS
|
||||
After=network.target postgresql.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=tranquil-pds
|
||||
Group=tranquil-pds
|
||||
EnvironmentFile=/etc/tranquil-pds/tranquil-pds.env
|
||||
ExecStart=/usr/local/bin/tranquil-pds
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
PrivateTmp=true
|
||||
ReadWritePaths=/var/lib/tranquil
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable tranquil-pds
|
||||
systemctl start tranquil-pds
|
||||
log_success "Tranquil PDS service started"
|
||||
|
||||
log_info "Installing nginx..."
|
||||
apt install -y nginx
|
||||
cat > /etc/nginx/sites-available/tranquil-pds << EOF
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name ${PDS_DOMAIN} *.${PDS_DOMAIN};
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/html;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade \$http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
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_read_timeout 86400;
|
||||
proxy_send_timeout 86400;
|
||||
client_max_body_size 100M;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
ln -sf /etc/nginx/sites-available/tranquil-pds /etc/nginx/sites-enabled/
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
nginx -t
|
||||
systemctl reload nginx
|
||||
log_success "nginx configured"
|
||||
|
||||
log_info "Configuring firewall..."
|
||||
apt install -y ufw
|
||||
ufw --force reset
|
||||
ufw default deny incoming
|
||||
ufw default allow outgoing
|
||||
ufw allow ssh
|
||||
ufw allow 80/tcp
|
||||
ufw allow 443/tcp
|
||||
ufw --force enable
|
||||
log_success "Firewall configured"
|
||||
|
||||
echo ""
|
||||
log_info "Obtaining wildcard SSL certificate..."
|
||||
echo ""
|
||||
echo "User handles are served as subdomains (eg., alice.${PDS_DOMAIN}),"
|
||||
echo "so you need a wildcard certificate. This requires DNS validation."
|
||||
echo ""
|
||||
echo "You'll need to add a TXT record to your DNS when prompted."
|
||||
echo ""
|
||||
read -p "Ready to proceed? (y/N): " CERT_READY
|
||||
|
||||
if [[ "$CERT_READY" =~ ^[Yy]$ ]]; then
|
||||
apt install -y certbot python3-certbot-nginx
|
||||
|
||||
log_info "Running certbot with DNS challenge..."
|
||||
echo ""
|
||||
echo "When prompted, add the TXT record to your DNS, wait a minute"
|
||||
echo "for propagation, then press Enter to continue."
|
||||
echo ""
|
||||
|
||||
if certbot certonly --manual --preferred-challenges dns \
|
||||
-d "${PDS_DOMAIN}" -d "*.${PDS_DOMAIN}" \
|
||||
--email "${CERTBOT_EMAIL}" --agree-tos; then
|
||||
|
||||
cat > /etc/nginx/sites-available/tranquil-pds << EOF
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name ${PDS_DOMAIN} *.${PDS_DOMAIN};
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/html;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://\$host\$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name ${PDS_DOMAIN} *.${PDS_DOMAIN};
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/${PDS_DOMAIN}/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/${PDS_DOMAIN}/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade \$http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
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_read_timeout 86400;
|
||||
proxy_send_timeout 86400;
|
||||
client_max_body_size 100M;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
nginx -t && systemctl reload nginx
|
||||
log_success "Wildcard SSL certificate installed"
|
||||
|
||||
echo ""
|
||||
log_warn "Certificate renewal note:"
|
||||
echo "Manual DNS challenges don't auto-renew. Before expiry, run:"
|
||||
echo " certbot renew --manual"
|
||||
echo ""
|
||||
echo "For auto-renewal, consider using a DNS provider plugin:"
|
||||
echo " apt install python3-certbot-dns-cloudflare # or your provider"
|
||||
echo ""
|
||||
else
|
||||
log_warn "Wildcard cert failed. You can retry later with:"
|
||||
echo " certbot certonly --manual --preferred-challenges dns \\"
|
||||
echo " -d ${PDS_DOMAIN} -d '*.${PDS_DOMAIN}'"
|
||||
fi
|
||||
else
|
||||
log_warn "Skipping SSL. Your PDS is running on HTTP only."
|
||||
echo "To add SSL later, run:"
|
||||
echo " certbot certonly --manual --preferred-challenges dns \\"
|
||||
echo " -d ${PDS_DOMAIN} -d '*.${PDS_DOMAIN}'"
|
||||
fi
|
||||
|
||||
log_info "Verifying installation..."
|
||||
sleep 3
|
||||
if curl -s "http://localhost:3000/xrpc/_health" | grep -q "version"; then
|
||||
log_success "Tranquil PDS is responding"
|
||||
else
|
||||
log_warn "Tranquil PDS may still be starting. Check: journalctl -u tranquil-pds -f"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_success "Installation complete"
|
||||
echo ""
|
||||
echo "PDS: https://${PDS_DOMAIN}"
|
||||
echo ""
|
||||
echo "Credentials (also in /etc/tranquil-pds/.credentials):"
|
||||
echo " DB password: ${DB_PASSWORD}"
|
||||
echo ""
|
||||
echo "Data locations:"
|
||||
echo " Blobs: /var/lib/tranquil/blobs"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " journalctl -u tranquil-pds -f # logs"
|
||||
echo " systemctl restart tranquil-pds # restart"
|
||||
echo " tranquil-pds-mailq # view trapped emails"
|
||||
echo ""
|
||||
Reference in New Issue
Block a user