{
"path": "/installing-an-at-protocol-pds-on-a-synology-nas-qzd29a9",
"site": "at://did:plc:v4zpi74gy7enfiwke7hmoxv5/site.standard.publication/3mf4rnsgoi2p6",
"tags": [
"Page",
"ATProto",
"Pinned"
],
"$type": "site.standard.document",
"title": "Installing an AT Protocol PDS on a Synology NAS",
"content": {
"blob": {
"$type": "blob",
"ref": {
"$link": "bafkreibzv5k64oimx5lc4snduowfgww5a6roqivrmf22iambgofzepiuiy"
},
"mimeType": "text/plain",
"size": 37620
},
"$type": "blog.pckt.content",
"references": [
{
"$type": "blob",
"ref": {
"$link": "bafkreieqkx2lbpjke6ff3rsqp56oymhwwvcu6vh7uovdnotajaypxrb2qu"
},
"mimeType": "image/jpeg",
"size": 220623
}
]
},
"updatedAt": "2026-02-18T10:02:54+00:00",
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreieqkx2lbpjke6ff3rsqp56oymhwwvcu6vh7uovdnotajaypxrb2qu"
},
"mimeType": "image/jpeg",
"size": 220623
},
"description": "My (completely biased) opinion is that most people who have a NAS at home own a Synology NAS. And I'm one of them! Take a moment to admire my DS716+II ! 🤩 A Synology NAS is the quiet workhorse humming in a corner somewhere, storing backups, media, half-finished side projects... When I realized I could run my own AT Protocol Personal Data Server (PDS), I had to try to put my trusty NAS to another good use. Running a PDS on a Synology NAS is great because:",
"publishedAt": "2026-02-13T19:36:53+00:00",
"textContent": "My (completely biased) opinion is that most people who have a NAS at home own a Synology NAS. And I'm one of them! Take a moment to admire my DS716+II ! 🤩\nA Synology NAS is the quiet workhorse humming in a corner somewhere, storing backups, media, half-finished side projects... When I realized I could run my own AT Protocol Personal Data Server (PDS), I had to try to put my trusty NAS to another good use.\nRunning a PDS on a Synology NAS is great because:\nPlenty of storage: Most of us have way more NAS storage than we’ll ever use. Perfect for blob storage.\nAlways on: The NAS is literally built to stay awake all year.\nCheap to run: You already paid for the hardware. Might as well get more value out of it.\nOwn your identity: No cloud host in the middle. Your Bluesky identity lives on your box.\nThe standard installer.sh script really doesn’t like Synology DSM (or maybe it's the other way around, I'm not very good with understanding relationships!)\nAnyways, here is how I got it working.\nWhy the Standard installer.sh Doesn't Work\nThe official PDS installer expects a “normal” Linux environment. Synology DSM is not that.\nHere are a few issues I ran into:\nSynology paths are different: Everything lives under /volume1/ or /volume2/.\nDocker behaves differently: DSM wraps Docker in its own UI and handles permissions its own way.\nPorts 80 and 443 are already used: DSM uses them for its web interface.\nNo systemd: The installer expects it and gets confused.\nPermissions can be tricky: It is easy to hit “permission denied” errors until the paths and ownership are set up properly.\nThis guide avoids those problems by doing a manual setup that plays nicely with DSM.\nPrerequisites\nBefore jumping in, make sure you have:\nA Synology NAS running DSM 7 or later\nAdministrator access to your NAS\nAdministrator access to your router\nA way to get a hostname (either a domain you own, or use Synology's free DDNS service)\nBasic SSH skills (being able to copy and paste commands is enough)\nNote: If your ISP gives you a dynamic IP (most do), Synology's built-in DDNS feature is perfect for this. You do not need to buy a domain name.\nPart 1: DSM Configuration\nStep 1: Enable SSH Access\nLog into DSM.\nOpen Control Panel → Terminal & SNMP.\nEnable SSH service.\nNote the port (default is 22).\nClick Apply.\nYou will use this to run the commands in the rest of the guide.\nStep 2: Install Docker\nOpen Package Center in DSM.\nSearch for Docker.\nInstall it.\nOpen Docker once to confirm it is working.\nStep 3: Create Required Directories\nSSH into your NAS:\nssh your-username@your-nas-ip\n\nThen create the folder structure:\nsudo mkdir -p /volume1/docker/bluesky-pds\nsudo mkdir -p /volume1/docker/bluesky-pds/pds-data\nsudo mkdir -p /volume1/docker/bluesky-pds/caddy/data\nsudo mkdir -p /volume1/docker/bluesky-pds/caddy/etc/caddy\n\nGive yourself permission to work in there (replace your-username with your real DSM username):\nsudo chown -R your-username:users /volume1/docker/bluesky-pds\n\nStep 4: Generate Caddy Instance UUID\nCaddy needs a UUID. Generate it like this:\ncd /volume1/docker/bluesky-pds\nuuidgen > caddy/data/caddy/instance.uuid\n\nIf uuidgen is missing, use Python:\npython3 -c \"import uuid; print(uuid.uuid4())\" > caddy/data/caddy/instance.uuid\n\nPart 2: Router Configuration\nDSM uses ports 80 and 443 for its own web interface, so the idea is:\nConfigure the router to expose ports 80 and 443 on the internet.\nConfigure the router to forward them to ports 8080 and 8443 on your NAS.\nStep 1: Find Your NAS IP\nIn DSM, go to Control Panel → Network → Network Interface and note your NAS IP address, for example 192.168.1.100.\nStep 2: Port Forwarding on Your Router\nIn your router web interface, create two port forwarding rules that point to your NAS IP.\nRule 1:\nService name: PDS HTTP\nExternal port: 80\nInternal IP: your NAS IP (for example 192.168.1.100)\nInternal port: 8080\nProtocol: TCP\nRule 2:\nService name: PDS HTTPS\nExternal port: 443\nInternal IP: your NAS IP\nInternal port: 8443\nProtocol: TCP\nFritz!Box users: Go to Internet → Port Sharing → Port Forwarding.\nStep 3: DNS Setup\nMost home internet connections have a dynamic IP address that changes periodically. If that is your situation (and it probably is), Synology's built-in Dynamic DNS (DDNS) feature is perfect for this.\nOption A: Dynamic DNS (Recommended for Most Users)\nIf your ISP gives you a dynamic IP, use Synology's DDNS service:\nIn DSM, go to Control Panel → External Access → DDNS.\nClick Add.\nSelect Synology as the service provider (or choose another provider if you prefer).\nEnter a hostname like yourname.synology.me (or whatever you want).\nClick Test Connection to verify it works.\nClick OK to save.\nIf you use Synology's DDNS, you get a hostname like yourname.synology.me. This works perfectly for your PDS. You can also use other DDNS providers (DuckDNS, No-IP, etc.) if you prefer.\nSynology will automatically update the DNS record whenever your public IP changes. The hostname you create here (e.g., yourname.synology.me) will be your PDS hostname.\nOption B: Static IP with Manual DNS\nIf you have a static IP address (or a domain you manage yourself):\nFind your public IP using a site like https://whatismyipaddress.com/.\nCreate an A record in your DNS provider:\nType: A\nName: yourname (or your chosen subdomain)\nValue: your public IP address\nTTL: 300 (or any reasonable value)\nIf you want multiple user handles later, add a wildcard:\nType: A\nName: *\nValue: your public IP address\nTTL: 300\nPart 3: PDS Configuration Files\nStep 1: docker-compose.yml\nCreate the file /volume1/docker/bluesky-pds/docker-compose.yml and put this in it:\nservices:\n caddy:\n container_name: caddy\n image: caddy:2\n depends_on:\n - pds\n restart: unless-stopped\n extra_hosts:\n # Allow Caddy to reach services exposed on the NAS host (e.g. Kuma on 3001)\n - \"host.docker.internal:host-gateway\"\n volumes:\n - type: bind\n source: /volume1/docker/bluesky-pds/caddy/data\n target: /data\n - type: bind\n source: /volume1/docker/bluesky-pds/caddy/etc/caddy\n target: /etc/caddy\n - type: bind\n source: /volume1/docker/bluesky-pds/site\n target: /srv/site\n ports:\n - \"8080:80\"\n - \"8443:443\"\n pds:\n container_name: pds\n image: ghcr.io/bluesky-social/pds:latest\n restart: unless-stopped\n volumes:\n - type: bind\n source: /volume1/docker/bluesky-pds\n target: /pds\n - type: bind\n source: /volume1/docker/bluesky-pds/pds-data\n target: /pds-data\n env_file:\n - ./pds.env\n pdsdash:\n container_name: pdsdash\n image: nginx:alpine\n restart: unless-stopped\n volumes:\n - type: bind\n source: /volume1/docker/bluesky-pds/pds-dash/dist\n target: /usr/share/nginx/html\n - type: bind\n source: /volume1/docker/bluesky-pds/pds-dash/nginx.conf\n target: /etc/nginx/conf.d/default.conf\n watchtower:\n container_name: watchtower\n image: ghcr.io/nicholas-fedor/watchtower:latest\n volumes:\n - type: bind\n source: /var/run/docker.sock\n target: /var/run/docker.sock\n restart: unless-stopped\n environment:\n WATCHTOWER_CLEANUP: true\n WATCHTOWER_SCHEDULE: \"@midnight\"\n\nStep 2: pds.env\nCreate /volume1/docker/bluesky-pds/pds.env with your configuration:\nPDS_HOSTNAME=yourname.synology.me\nPDS_ADMIN_EMAIL=your-email@example.com\nPDS_ADMIN_PASSWORD=YourSecurePassword123!\nPDS_JWT_SECRET=your-64-character-hex-secret-here\nPDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX=your-64-character-hex-key-here\nPDS_EMAIL_SMTP_URL=smtp://user:password@smtp.example.com:587\nPDS_EMAIL_FROM_ADDRESS=no-reply@yourdomain.com\nPDS_DATA_DIRECTORY=/pds-data\nPDS_BLOBSTORE_DISK_LOCATION=/pds-data/blobs\nPDS_BLOBSTORE_DISK_TMP=/pds-data/tmp\nPDS_SQLITE_LOCATION=/pds-data/pds.sqlite\nPDS_BLOB_UPLOAD_LIMIT=104857600\nPDS_ENABLE_ACCOUNT_REGISTRATION=true\nPDS_DID_PLC_URL=https://plc.directory\nPDS_BSKY_APP_VIEW_URL=https://api.bsky.app\nPDS_BSKY_APP_VIEW_DID=did:web:api.bsky.app\nPDS_REPORT_SERVICE_URL=https://mod.bsky.app\nPDS_REPORT_SERVICE_DID=did:plc:ar7c4by46qjdydhdevvrndac\nPDS_CRAWLERS=https://bsky.network\nLOG_LEVEL=info\nLOG_ENABLED=true\n\nReplace these values:\nPDS_HOSTNAME: your domain name (if using Synology DDNS, this will be something like yourname.synology.me)\nPDS_ADMIN_EMAIL: your email address\nPDS_ADMIN_PASSWORD: a strong password\nPDS_JWT_SECRET: a 64 character hex string generated with openssl rand -hex 32\nPDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX: another 64 character hex string\nPDS_EMAIL_SMTP_URL and PDS_EMAIL_FROM_ADDRESS: your SMTP details if you want email support\nStep 3: Caddyfile\nCreate /volume1/docker/bluesky-pds/caddy/etc/caddy/Caddyfile:\n{\n email your-email@example.com\n on_demand_tls {\n ask http://pds:3000/tls-check\n }\n}\n\nyourname.synology.me, *.yourname.synology.me {\n tls {\n on_demand\n }\n encode gzip zstd\n reverse_proxy http://pds:3000\n}\n\nhello.yourname.synology.me {\n tls your-email@example.com\n encode gzip zstd\n reverse_proxy http://pdsdash:80\n}\n\nReplace yourname.synology.me with your domain (or your Synology DDNS hostname if you went that route) and your-email@example.com with your email address.\nPart 4: Deployment\nStep 1: Start the Services\nFrom your SSH session on the NAS:\ncd /volume1/docker/bluesky-pds\nsudo docker compose pull\nsudo docker compose up -d\n\nStep 2: Check Services\nsudo docker compose ps\n\nYou should see all containers with status Up.\nStep 3: Watch Logs\nCheck the PDS logs:\nsudo docker compose logs -f pds\n\nPress Ctrl+C to exit.\nCheck Caddy logs:\nsudo docker compose logs -f caddy\n\nStep 4: Test the Health Endpoint\nFrom another machine (replace with your actual hostname):\ncurl -vk --resolve yourname.synology.me:443:YOUR_PUBLIC_IP \\\nhttps://yourname.synology.me/xrpc/_health\n\nOr if you want to test without specifying the IP:\ncurl https://yourname.synology.me/xrpc/_health\n\nYou should get a JSON response like:\n{\"version\":\"0.4.x\"}\n\nYou can also open https://yourname.synology.me/ (or your DDNS hostname) in a browser to see the PDS banner.\nPart 5: Creating Invites and First User\nThe goat CLI tool is preinstalled inside the PDS container. All commands here run on the NAS.\nStep 1: Create an Invite Code\ncd /volume1/docker/bluesky-pds\nsudo docker compose exec pds goat pds admin create-invites \\\n --count 1 \\\n --uses 1\n\nThis will output something like:\ncode: yourname-synology-me-xxxxxx-xxxx\nuses: 1\n\nKeep that code somewhere safe.\nTo create more invite codes in one go:\nsudo docker compose exec pds goat pds admin create-invites \\\n --count 5 \\\n --uses 3\n\nStep 2: Create Your First User\nUse your invite code to create your account:\nsudo docker compose exec pds goat account create \\\n --pds-host https://yourname.synology.me \\\n --handle alice.yourname.synology.me \\\n --email your-email@example.com \\\n --password 'YourSecurePassword123!' \\\n --invite-code yourname-synology-me-xxxxxx-xxxx\n\nOf course, you'll need to replace:\nalice.yourname.synology.me with your handle (it must fit the *.yourdomain pattern).\nyour-email@example.com with your email.\nYourSecurePassword123! with your password.\nyourname-synology-me-xxxxxx-xxxx with your actual invite code.\nOn success, goat will print your DID, which looks like did:plc:something.\nStep 3: List Accounts\nsudo docker compose exec pds goat account list \\\n --pds-host https://yourname.synology.me \\\n --admin-password 'YourSecurePassword123!'\n\nUse the admin password you set in pds.env.\nStep 4: List Invite Codes\nsudo docker compose exec pds goat pds admin list-invites\n\nTroubleshooting\nPort Conflicts\nIf Docker complains about ports, check what is using them:\nsudo netstat -tuln | grep -E '8080|8443'\n\nDNS and Certificates\nIf TLS certificates fail:\nIf using DDNS, check that it is updating correctly in Control Panel → External Access → DDNS.\nCheck DNS resolution: dig yourname.synology.me (or your hostname).\nTest port forwarding with tools like telnet or nc.\nInspect Caddy logs:\nsudo docker compose logs caddy\n\nPermission Issues\nIf you see permission errors, reset ownership and permissions:\nsudo chown -R $(whoami):users /volume1/docker/bluesky-pds\nsudo chmod -R 755 /volume1/docker/bluesky-pds\n\nContainers Do Not Start\nCheck logs:\nsudo docker compose logs pds\nsudo docker compose logs caddy\n\nRestart everything:\nsudo docker compose down\nsudo docker compose up -d\n\nMaintenance\nView Logs\n# PDS logs\nsudo docker compose logs -f pds\n\n# Caddy logs\nsudo docker compose logs -f caddy\n\n# All services\nsudo docker compose logs -f\n\nUpdate PDS\nWatchtower will update images regularly, but you can also do it manually:\ncd /volume1/docker/bluesky-pds\nsudo docker compose pull\nsudo docker compose up -d\n\nBackups\nBack up your PDS data regularly:\nsudo tar -czf /volume1/backups/pds-backup-$(date +%Y%m%d).tar.gz \\\n/volume1/docker/bluesky-pds/pds-data\n\nSecurity Tips\nRotate secrets after the initial setup.\nUse strong passwords for admin and user accounts.\nRestrict SSH access if possible.\nKeep DSM and Docker up to date.\nNext Steps\nMigrate an existing Bluesky account using the official migration guide.\nInvite friends by generating more invite codes.\nSet up monitoring using the pdsdash dashboard.\nAutomate your backup process.\nConclusion\nWow! that was quite a mouthful, and honestly, from the length of this, it might look difficult but it really is not. If you got this far, you now have a working Bluesky PDS running on your Synology NAS. No rented VPS, no mystery cloud, just your hardware, your data, and your identity.\nIt feels good to run your own corner of the network.\nAdditional Resources\nBluesky PDS GitHub Repository\nATProto Documentation\nSynology Docker Documentation"
}