-2
README.md
-2
README.md
···
53
| Guide | Best For |
54
|-------|----------|
55
| [Debian](docs/install-debian.md) | Debian 13+ with systemd |
56
-
| [Alpine](docs/install-alpine.md) | Alpine 3.23+ with OpenRC |
57
-
| [OpenBSD](docs/install-openbsd.md) | OpenBSD 7.8+ with rc.d |
58
| [Containers](docs/install-containers.md) | Podman with quadlets or OpenRC |
59
| [Kubernetes](docs/install-kubernetes.md) | You know what you're doing |
60
-265
docs/install-alpine.md
-265
docs/install-alpine.md
···
1
-
# Tranquil PDS Production Installation on Alpine Linux
2
-
> **Warning**: These instructions are untested and theoretical, written from the top of Lewis' head. They may contain errors or omissions. This warning will be removed once the guide has been verified.
3
-
4
-
This guide covers installing Tranquil PDS on Alpine Linux 3.23.
5
-
6
-
## Prerequisites
7
-
- A VPS with at least 2GB RAM and 20GB disk
8
-
- A domain name pointing to your server's IP
9
-
- A **wildcard TLS certificate** for `*.pds.example.com` (user handles are served as subdomains)
10
-
- Root access
11
-
## 1. System Setup
12
-
```sh
13
-
apk update && apk upgrade
14
-
apk add curl git build-base openssl-dev pkgconf
15
-
```
16
-
## 2. Install Rust
17
-
```sh
18
-
apk add rustup
19
-
rustup-init -y
20
-
source ~/.cargo/env
21
-
rustup default stable
22
-
```
23
-
This installs the latest stable Rust. Alpine also ships Rust via `apk add rust cargo` if you prefer system packages.
24
-
## 3. Install postgres
25
-
```sh
26
-
apk add postgresql postgresql-contrib
27
-
rc-update add postgresql
28
-
/etc/init.d/postgresql setup
29
-
rc-service postgresql start
30
-
psql -U postgres -c "CREATE USER tranquil_pds WITH PASSWORD 'your-secure-password';"
31
-
psql -U postgres -c "CREATE DATABASE pds OWNER tranquil_pds;"
32
-
psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE pds TO tranquil_pds;"
33
-
```
34
-
## 4. Install minio
35
-
```sh
36
-
curl -O https://dl.min.io/server/minio/release/linux-amd64/minio
37
-
chmod +x minio
38
-
mv minio /usr/local/bin/
39
-
mkdir -p /var/lib/minio/data
40
-
adduser -D -H -s /sbin/nologin minio-user
41
-
chown -R minio-user:minio-user /var/lib/minio
42
-
cat > /etc/conf.d/minio << 'EOF'
43
-
MINIO_ROOT_USER="minioadmin"
44
-
MINIO_ROOT_PASSWORD="your-minio-password"
45
-
MINIO_VOLUMES="/var/lib/minio/data"
46
-
MINIO_OPTS="--console-address :9001"
47
-
EOF
48
-
cat > /etc/init.d/minio << 'EOF'
49
-
#!/sbin/openrc-run
50
-
name="minio"
51
-
description="MinIO Object Storage"
52
-
command="/usr/local/bin/minio"
53
-
command_args="server ${MINIO_VOLUMES} ${MINIO_OPTS}"
54
-
command_user="minio-user"
55
-
command_background=true
56
-
pidfile="/run/${RC_SVCNAME}.pid"
57
-
output_log="/var/log/minio.log"
58
-
error_log="/var/log/minio.log"
59
-
depend() {
60
-
need net
61
-
}
62
-
start_pre() {
63
-
. /etc/conf.d/minio
64
-
export MINIO_ROOT_USER MINIO_ROOT_PASSWORD
65
-
}
66
-
EOF
67
-
chmod +x /etc/init.d/minio
68
-
rc-update add minio
69
-
rc-service minio start
70
-
```
71
-
Create the buckets (wait a few seconds for minio to start):
72
-
```sh
73
-
curl -O https://dl.min.io/client/mc/release/linux-amd64/mc
74
-
chmod +x mc
75
-
mv mc /usr/local/bin/
76
-
mc alias set local http://localhost:9000 minioadmin your-minio-password
77
-
mc mb local/pds-blobs
78
-
mc mb local/pds-backups
79
-
```
80
-
## 5. Install valkey
81
-
```sh
82
-
apk add valkey
83
-
rc-update add valkey
84
-
rc-service valkey start
85
-
```
86
-
## 6. Install deno (for frontend build)
87
-
```sh
88
-
curl -fsSL https://deno.land/install.sh | sh
89
-
export PATH="$HOME/.deno/bin:$PATH"
90
-
echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.profile
91
-
```
92
-
## 7. Clone and Build Tranquil PDS
93
-
```sh
94
-
mkdir -p /opt && cd /opt
95
-
git clone https://tangled.org/lewis.moe/bspds-sandbox tranquil-pds
96
-
cd tranquil-pds
97
-
cd frontend
98
-
deno task build
99
-
cd ..
100
-
cargo build --release
101
-
```
102
-
## 8. Install sqlx-cli and Run Migrations
103
-
```sh
104
-
cargo install sqlx-cli --no-default-features --features postgres
105
-
export DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds"
106
-
sqlx migrate run
107
-
```
108
-
## 9. Configure Tranquil PDS
109
-
```sh
110
-
mkdir -p /etc/tranquil-pds
111
-
cp /opt/tranquil-pds/.env.example /etc/tranquil-pds/tranquil-pds.env
112
-
chmod 600 /etc/tranquil-pds/tranquil-pds.env
113
-
```
114
-
Edit `/etc/tranquil-pds/tranquil-pds.env` and fill in your values. Generate secrets with:
115
-
```sh
116
-
openssl rand -base64 48
117
-
```
118
-
## 10. Create OpenRC Service
119
-
```sh
120
-
adduser -D -H -s /sbin/nologin tranquil-pds
121
-
cp /opt/tranquil-pds/target/release/tranquil-pds /usr/local/bin/
122
-
mkdir -p /var/lib/tranquil-pds
123
-
cp -r /opt/tranquil-pds/frontend/dist /var/lib/tranquil-pds/frontend
124
-
chown -R tranquil-pds:tranquil-pds /var/lib/tranquil-pds
125
-
cat > /etc/init.d/tranquil-pds << 'EOF'
126
-
#!/sbin/openrc-run
127
-
name="tranquil-pds"
128
-
description="Tranquil PDS - AT Protocol PDS"
129
-
command="/usr/local/bin/tranquil-pds"
130
-
command_user="tranquil-pds"
131
-
command_background=true
132
-
pidfile="/run/${RC_SVCNAME}.pid"
133
-
output_log="/var/log/tranquil-pds.log"
134
-
error_log="/var/log/tranquil-pds.log"
135
-
depend() {
136
-
need net postgresql minio
137
-
}
138
-
start_pre() {
139
-
export FRONTEND_DIR=/var/lib/tranquil-pds/frontend
140
-
. /etc/tranquil-pds/tranquil-pds.env
141
-
export SERVER_HOST SERVER_PORT PDS_HOSTNAME DATABASE_URL
142
-
export S3_ENDPOINT AWS_REGION S3_BUCKET AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY
143
-
export VALKEY_URL JWT_SECRET DPOP_SECRET MASTER_KEY CRAWLERS
144
-
}
145
-
EOF
146
-
chmod +x /etc/init.d/tranquil-pds
147
-
rc-update add tranquil-pds
148
-
rc-service tranquil-pds start
149
-
```
150
-
## 11. Install and Configure nginx
151
-
```sh
152
-
apk add nginx certbot certbot-nginx
153
-
cat > /etc/nginx/http.d/tranquil-pds.conf << 'EOF'
154
-
server {
155
-
listen 80;
156
-
listen [::]:80;
157
-
server_name pds.example.com;
158
-
location / {
159
-
proxy_pass http://127.0.0.1:3000;
160
-
proxy_http_version 1.1;
161
-
proxy_set_header Upgrade $http_upgrade;
162
-
proxy_set_header Connection "upgrade";
163
-
proxy_set_header Host $host;
164
-
proxy_set_header X-Real-IP $remote_addr;
165
-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
166
-
proxy_set_header X-Forwarded-Proto $scheme;
167
-
proxy_read_timeout 86400;
168
-
}
169
-
}
170
-
EOF
171
-
rc-update add nginx
172
-
rc-service nginx start
173
-
```
174
-
## 12. Obtain Wildcard SSL Certificate
175
-
User handles are served as subdomains (e.g., `alice.pds.example.com`), so you need a wildcard certificate.
176
-
177
-
Wildcard certs require DNS-01 validation. For manual DNS validation (works with any provider):
178
-
```sh
179
-
certbot certonly --manual --preferred-challenges dns \
180
-
-d pds.example.com -d '*.pds.example.com'
181
-
```
182
-
Follow the prompts to add TXT records to your DNS.
183
-
184
-
If your DNS provider has a certbot plugin, you can use that for auto-renewal:
185
-
```sh
186
-
apk add certbot-dns-cloudflare
187
-
certbot certonly --dns-cloudflare \
188
-
--dns-cloudflare-credentials /etc/cloudflare.ini \
189
-
-d pds.example.com -d '*.pds.example.com'
190
-
```
191
-
192
-
After obtaining the cert, update nginx to use it, then set up auto-renewal:
193
-
```sh
194
-
echo "0 0 * * * certbot renew --quiet && rc-service nginx reload" | crontab -
195
-
```
196
-
## 13. Configure Firewall
197
-
```sh
198
-
apk add iptables ip6tables
199
-
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
200
-
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
201
-
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
202
-
iptables -A INPUT -i lo -j ACCEPT
203
-
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
204
-
iptables -P INPUT DROP
205
-
ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT
206
-
ip6tables -A INPUT -p tcp --dport 80 -j ACCEPT
207
-
ip6tables -A INPUT -p tcp --dport 443 -j ACCEPT
208
-
ip6tables -A INPUT -i lo -j ACCEPT
209
-
ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
210
-
ip6tables -P INPUT DROP
211
-
rc-update add iptables
212
-
rc-update add ip6tables
213
-
/etc/init.d/iptables save
214
-
/etc/init.d/ip6tables save
215
-
```
216
-
## 14. Verify Installation
217
-
```sh
218
-
rc-service tranquil-pds status
219
-
curl -s https://pds.example.com/xrpc/_health
220
-
curl -s https://pds.example.com/.well-known/atproto-did
221
-
```
222
-
## Maintenance
223
-
View logs:
224
-
```sh
225
-
tail -f /var/log/tranquil-pds.log
226
-
```
227
-
Update Tranquil PDS:
228
-
```sh
229
-
cd /opt/tranquil-pds
230
-
git pull
231
-
cd frontend && deno task build && cd ..
232
-
cargo build --release
233
-
rc-service tranquil-pds stop
234
-
cp target/release/tranquil-pds /usr/local/bin/
235
-
cp -r frontend/dist /var/lib/tranquil-pds/frontend
236
-
DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds" sqlx migrate run
237
-
rc-service tranquil-pds start
238
-
```
239
-
Backup database:
240
-
```sh
241
-
pg_dump -U postgres pds > /var/backups/pds-$(date +%Y%m%d).sql
242
-
```
243
-
244
-
## Custom Homepage
245
-
246
-
Drop a `homepage.html` in `/var/lib/tranquil-pds/frontend/` and it becomes your landing page. Go nuts with it. Account dashboard is at `/app/` so you won't break anything.
247
-
248
-
```sh
249
-
cat > /var/lib/tranquil-pds/frontend/homepage.html << 'EOF'
250
-
<!DOCTYPE html>
251
-
<html>
252
-
<head>
253
-
<title>Welcome to my PDS</title>
254
-
<style>
255
-
body { font-family: system-ui; max-width: 600px; margin: 100px auto; padding: 20px; }
256
-
</style>
257
-
</head>
258
-
<body>
259
-
<h1>Welcome to my amazing zoo pen</h1>
260
-
<p>This is a <a href="https://atproto.com">AT Protocol</a> Personal Data Server.</p>
261
-
<p><a href="/app/">Sign in</a> or learn more at <a href="https://bsky.social">Bluesky</a>.</p>
262
-
</body>
263
-
</html>
264
-
EOF
265
-
```
···
-277
docs/install-openbsd.md
-277
docs/install-openbsd.md
···
1
-
# Tranquil PDS Production Installation on OpenBSD
2
-
> **Warning**: These instructions are untested and theoretical, written from the top of Lewis' head. They may contain errors or omissions. This warning will be removed once the guide has been verified.
3
-
This guide covers installing Tranquil PDS on OpenBSD 7.8.
4
-
## Prerequisites
5
-
- A VPS with at least 2GB RAM and 20GB disk
6
-
- A domain name pointing to your server's IP
7
-
- A **wildcard TLS certificate** for `*.pds.example.com` (user handles are served as subdomains)
8
-
- Root access (or doas configured)
9
-
## Why nginx over relayd?
10
-
OpenBSD's native `relayd` supports WebSockets but does **not** support HTTP/2. For a modern PDS deployment, we recommend nginx which provides HTTP/2, WebSocket support, and automatic OCSP stapling.
11
-
## 1. System Setup
12
-
```sh
13
-
pkg_add curl git
14
-
```
15
-
## 2. Install Rust
16
-
```sh
17
-
pkg_add rust
18
-
```
19
-
OpenBSD ships Rust in ports. For the latest stable, use rustup:
20
-
```sh
21
-
pkg_add rustup
22
-
rustup-init -y
23
-
source ~/.cargo/env
24
-
rustup default stable
25
-
```
26
-
## 3. Install postgres
27
-
```sh
28
-
pkg_add postgresql-server postgresql-client
29
-
mkdir -p /var/postgresql/data
30
-
chown _postgresql:_postgresql /var/postgresql/data
31
-
su - _postgresql -c "initdb -D /var/postgresql/data -U postgres -A scram-sha-256"
32
-
rcctl enable postgresql
33
-
rcctl start postgresql
34
-
psql -U postgres -c "CREATE USER tranquil_pds WITH PASSWORD 'your-secure-password';"
35
-
psql -U postgres -c "CREATE DATABASE pds OWNER tranquil_pds;"
36
-
psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE pds TO tranquil_pds;"
37
-
```
38
-
## 4. Install minio
39
-
OpenBSD doesn't have a minio package. Options:
40
-
**Option A: Use an external S3-compatible service (recommended for production)**
41
-
aws s3, backblaze b2, or upcloud managed object storage. Skip to step 5 and configure the S3 credentials in step 9.
42
-
**Option B: Build minio from source**
43
-
```sh
44
-
pkg_add go
45
-
mkdir -p /tmp/minio-build && cd /tmp/minio-build
46
-
ftp -o minio.tar.gz https://github.com/minio/minio/archive/refs/tags/RELEASE.2025-10-15T17-29-55Z.tar.gz
47
-
tar xzf minio.tar.gz
48
-
cd minio-*
49
-
go build -o minio .
50
-
cp minio /usr/local/bin/
51
-
mkdir -p /var/minio/data
52
-
useradd -d /var/minio -s /sbin/nologin _minio
53
-
chown -R _minio:_minio /var/minio
54
-
cat > /etc/minio.conf << 'EOF'
55
-
MINIO_ROOT_USER=minioadmin
56
-
MINIO_ROOT_PASSWORD=your-minio-password
57
-
EOF
58
-
chmod 600 /etc/minio.conf
59
-
cat > /etc/rc.d/minio << 'EOF'
60
-
#!/bin/ksh
61
-
daemon="/usr/local/bin/minio"
62
-
daemon_user="_minio"
63
-
daemon_flags="server /var/minio/data --console-address :9001"
64
-
. /etc/rc.d/rc.subr
65
-
rc_pre() {
66
-
. /etc/minio.conf
67
-
export MINIO_ROOT_USER MINIO_ROOT_PASSWORD
68
-
}
69
-
rc_cmd $1
70
-
EOF
71
-
chmod +x /etc/rc.d/minio
72
-
rcctl enable minio
73
-
rcctl start minio
74
-
```
75
-
Create the buckets:
76
-
```sh
77
-
ftp -o /usr/local/bin/mc https://dl.min.io/client/mc/release/openbsd-amd64/mc
78
-
chmod +x /usr/local/bin/mc
79
-
mc alias set local http://localhost:9000 minioadmin your-minio-password
80
-
mc mb local/pds-blobs
81
-
mc mb local/pds-backups
82
-
```
83
-
## 5. Install redis
84
-
OpenBSD has redis in ports (valkey not available yet):
85
-
```sh
86
-
pkg_add redis
87
-
rcctl enable redis
88
-
rcctl start redis
89
-
```
90
-
## 6. Install deno (for frontend build)
91
-
```sh
92
-
curl -fsSL https://deno.land/install.sh | sh
93
-
export PATH="$HOME/.deno/bin:$PATH"
94
-
echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.profile
95
-
```
96
-
## 7. Clone and Build Tranquil PDS
97
-
```sh
98
-
mkdir -p /opt && cd /opt
99
-
git clone https://tangled.org/lewis.moe/bspds-sandbox tranquil-pds
100
-
cd tranquil-pds
101
-
cd frontend
102
-
deno task build
103
-
cd ..
104
-
cargo build --release
105
-
```
106
-
## 8. Install sqlx-cli and Run Migrations
107
-
```sh
108
-
cargo install sqlx-cli --no-default-features --features postgres
109
-
export DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds"
110
-
sqlx migrate run
111
-
```
112
-
## 9. Configure Tranquil PDS
113
-
```sh
114
-
mkdir -p /etc/tranquil-pds
115
-
cp /opt/tranquil-pds/.env.example /etc/tranquil-pds/tranquil-pds.conf
116
-
chmod 600 /etc/tranquil-pds/tranquil-pds.conf
117
-
```
118
-
Edit `/etc/tranquil-pds/tranquil-pds.conf` and fill in your values. Generate secrets with:
119
-
```sh
120
-
openssl rand -base64 48
121
-
```
122
-
## 10. Create rc.d Service
123
-
```sh
124
-
useradd -d /var/empty -s /sbin/nologin _tranquil_pds
125
-
cp /opt/tranquil-pds/target/release/tranquil-pds /usr/local/bin/
126
-
mkdir -p /var/tranquil-pds
127
-
cp -r /opt/tranquil-pds/frontend/dist /var/tranquil-pds/frontend
128
-
chown -R _tranquil_pds:_tranquil_pds /var/tranquil-pds
129
-
cat > /etc/rc.d/tranquil_pds << 'EOF'
130
-
#!/bin/ksh
131
-
daemon="/usr/local/bin/tranquil-pds"
132
-
daemon_user="_tranquil_pds"
133
-
daemon_logger="daemon.info"
134
-
. /etc/rc.d/rc.subr
135
-
rc_pre() {
136
-
export FRONTEND_DIR=/var/tranquil-pds/frontend
137
-
while IFS='=' read -r key value; do
138
-
case "$key" in
139
-
\#*|"") continue ;;
140
-
esac
141
-
export "$key=$value"
142
-
done < /etc/tranquil-pds/tranquil-pds.conf
143
-
}
144
-
rc_cmd $1
145
-
EOF
146
-
chmod +x /etc/rc.d/tranquil_pds
147
-
rcctl enable tranquil_pds
148
-
rcctl start tranquil_pds
149
-
```
150
-
## 11. Install and Configure nginx
151
-
```sh
152
-
pkg_add nginx
153
-
cat > /etc/nginx/nginx.conf << 'EOF'
154
-
worker_processes 1;
155
-
events {
156
-
worker_connections 1024;
157
-
}
158
-
http {
159
-
include mime.types;
160
-
server {
161
-
listen 80;
162
-
listen [::]:80;
163
-
server_name pds.example.com;
164
-
location /.well-known/acme-challenge/ {
165
-
root /var/www/acme;
166
-
}
167
-
location / {
168
-
return 301 https://$host$request_uri;
169
-
}
170
-
}
171
-
server {
172
-
listen 443 ssl http2;
173
-
listen [::]:443 ssl http2;
174
-
server_name pds.example.com;
175
-
ssl_certificate /etc/ssl/pds.example.com.fullchain.pem;
176
-
ssl_certificate_key /etc/ssl/private/pds.example.com.key;
177
-
ssl_protocols TLSv1.2 TLSv1.3;
178
-
ssl_ciphers HIGH:!aNULL:!MD5;
179
-
ssl_prefer_server_ciphers on;
180
-
ssl_session_cache shared:SSL:10m;
181
-
location / {
182
-
proxy_pass http://127.0.0.1:3000;
183
-
proxy_http_version 1.1;
184
-
proxy_set_header Upgrade $http_upgrade;
185
-
proxy_set_header Connection "upgrade";
186
-
proxy_set_header Host $host;
187
-
proxy_set_header X-Real-IP $remote_addr;
188
-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
189
-
proxy_set_header X-Forwarded-Proto $scheme;
190
-
proxy_read_timeout 86400;
191
-
}
192
-
}
193
-
}
194
-
EOF
195
-
mkdir -p /var/www/acme
196
-
rcctl enable nginx
197
-
```
198
-
## 12. Obtain Wildcard SSL Certificate
199
-
User handles are served as subdomains (e.g., `alice.pds.example.com`), so you need a wildcard certificate.
200
-
201
-
OpenBSD's native `acme-client` only supports HTTP-01 validation, which can't issue wildcard certs. You have a few options:
202
-
203
-
**Option A: Use certbot with DNS validation (recommended)**
204
-
```sh
205
-
pkg_add certbot
206
-
certbot certonly --manual --preferred-challenges dns \
207
-
-d pds.example.com -d '*.pds.example.com'
208
-
```
209
-
Follow the prompts to add TXT records to your DNS. Then update nginx.conf to point to the certbot certs.
210
-
211
-
**Option B: Use a managed DNS provider with API**
212
-
If your DNS provider has a certbot plugin, you can automate renewal.
213
-
214
-
**Option C: Use acme.sh**
215
-
[acme.sh](https://github.com/acmesh-official/acme.sh) supports many DNS providers for automated wildcard cert renewal.
216
-
217
-
After obtaining the cert, update nginx to use it and restart:
218
-
```sh
219
-
rcctl restart nginx
220
-
```
221
-
## 13. Configure Packet Filter (pf)
222
-
```sh
223
-
cat >> /etc/pf.conf << 'EOF'
224
-
pass in on egress proto tcp from any to any port { 22, 80, 443 }
225
-
EOF
226
-
pfctl -f /etc/pf.conf
227
-
```
228
-
## 14. Verify Installation
229
-
```sh
230
-
rcctl check tranquil_pds
231
-
ftp -o - https://pds.example.com/xrpc/_health
232
-
ftp -o - https://pds.example.com/.well-known/atproto-did
233
-
```
234
-
## Maintenance
235
-
View logs:
236
-
```sh
237
-
tail -f /var/log/daemon
238
-
```
239
-
Update Tranquil PDS:
240
-
```sh
241
-
cd /opt/tranquil-pds
242
-
git pull
243
-
cd frontend && deno task build && cd ..
244
-
cargo build --release
245
-
rcctl stop tranquil_pds
246
-
cp target/release/tranquil-pds /usr/local/bin/
247
-
cp -r frontend/dist /var/tranquil-pds/frontend
248
-
DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds" sqlx migrate run
249
-
rcctl start tranquil_pds
250
-
```
251
-
Backup database:
252
-
```sh
253
-
pg_dump -U postgres pds > /var/backups/pds-$(date +%Y%m%d).sql
254
-
```
255
-
256
-
## Custom Homepage
257
-
258
-
Drop a `homepage.html` in `/var/tranquil-pds/frontend/` and it becomes your landing page. Go nuts with it. Account dashboard is at `/app/` so you won't break anything.
259
-
260
-
```sh
261
-
cat > /var/tranquil-pds/frontend/homepage.html << 'EOF'
262
-
<!DOCTYPE html>
263
-
<html>
264
-
<head>
265
-
<title>Welcome to my PDS</title>
266
-
<style>
267
-
body { font-family: system-ui; max-width: 600px; margin: 100px auto; padding: 20px; }
268
-
</style>
269
-
</head>
270
-
<body>
271
-
<h1>Welcome to my uma musume shipping site!</h1>
272
-
<p>This is a <a href="https://atproto.com">AT Protocol</a> Personal Data Server.</p>
273
-
<p><a href="/app/">Sign in</a> or learn more at <a href="https://bsky.social">Bluesky</a>.</p>
274
-
</body>
275
-
</html>
276
-
EOF
277
-
```
···