Self-hosting a bluesky personal data server on ubuntu vpsThis article provides a guide for self-hosting a Bluesky Personal Data Server on Ubuntu VPS.

Table of Contents

What is Bluesky?

Bluesky is a decentralized social networking protocol designed to create a more open and user-controlled internet. It was initially funded by Twitter in 2019 but later became an independent organization. Bluesky aims to move away from centralized control of social media platforms, allowing users to own their data, choose their algorithms, and self-host their identities.

Bluesky vs. Other Social Media

Feature Bluesky (AT Protocol) Twitter (X) Mastodon (ActivityPub)
Decentralized ✅ Yes ❌ No ✅ Yes
User-Controlled Algorithms ✅ Yes ❌ No ❌ No
Data Ownership ✅ User-Owned ❌ Platform-Owned ✅ User-Owned
Federation ✅ Yes ❌ No ✅ Yes
Self-Hosting Possible ✅ Yes ❌ No ✅ Yes

Step-by-Step Guide: Self-Hosting a Bluesky Personal Data Server on Ubuntu VPS

Bluesky’s decentralized social networking protocol (AT Protocol) allows users to self-host their own personal data servers (PDS). This guide provides a step-by-step process for self-hosting a Bluesky personal data server on Ubuntu VPS.

Prerequisites

Before starting, ensure you have:

Launch 100% ssd ubuntu vps from $2. 49/mo!

  1. Set Up Your Ubuntu VPS

    1. Update and Upgrade Ubuntu

      First, update your system to ensure you have the latest packages:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      sudo apt update && sudo apt upgrade -y
      sudo apt update && sudo apt upgrade -y
      sudo apt update && sudo apt upgrade -y
    2. Install Required Dependencies

      Ensure basic tools are installed:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      sudo apt install -y curl git unzip wget build-essential
      sudo apt install -y curl git unzip wget build-essential
      sudo apt install -y curl git unzip wget build-essential
  2. Set Up a User for Bluesky PDS

    It’s good practice to create a non-root user to manage the server:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    sudo adduser bluesky sudo usermod -aG sudo bluesky
    sudo adduser bluesky sudo usermod -aG sudo bluesky
    sudo adduser bluesky sudo usermod -aG sudo bluesky

    Switch to the new user:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    su - bluesky
    su - bluesky
    su - bluesky
  3. Install Docker and Docker Compose

    Bluesky’s PDS (Personal Data Server) can run inside a Docker container.

    1. Install Docker

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      curl -fsSL https://get.docker.com | sudo bash sudo usermod -aG docker $(whoami)
      curl -fsSL https://get.docker.com | sudo bash sudo usermod -aG docker $(whoami)
      curl -fsSL https://get.docker.com | sudo bash sudo usermod -aG docker $(whoami)

      Log out and log back in for changes to take effect.

    2. Install Docker Compose

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      sudo apt install -y docker-compose
      sudo apt install -y docker-compose
      sudo apt install -y docker-compose

      Verify installation:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      docker --version docker-compose --version
      docker --version docker-compose --version
      docker --version docker-compose --version
  4. Clone the Bluesky PDS Repository

    Navigate to your home directory and clone the official Bluesky PDS repository:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    git clone https://github.com/bluesky-social/pds.git cd pds
    git clone https://github.com/bluesky-social/pds.git cd pds
    git clone https://github.com/bluesky-social/pds.git cd pds
  5. Configure Environment Variables

    Copy the sample environment file:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    cp .env.example .env
    cp .env.example .env
    cp .env.example .env
    

    Edit the .env file using nano or vim:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    nano .env
    nano .env
    nano .env
    

    Modify key variables:

    • ADMIN_PASSWORD (Set a strong admin password)
    • PDS_HOSTNAME (Set your domain, e.g., pds.yourdomain.com)
    • DBHOST, DBUSER, DB_PASSWORD (for PostgreSQL database)
    • EMAIL_SERVER (for user email verification)

    Save and exit (CTRL + X, then Y, then Enter).

  6. Set Up PostgreSQL Database

    Bluesky PDS requires PostgreSQL.

    1. Install PostgreSQL

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      sudo apt install -y postgresql postgresql-contrib
      sudo apt install -y postgresql postgresql-contrib
      sudo apt install -y postgresql postgresql-contrib
    2. Create a Database and User

      Access PostgreSQL:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      sudo -u postgres psql
      sudo -u postgres psql
      sudo -u postgres psql

      Run the following commands:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      CREATE DATABASE bluesky_pds; CREATE USER bluesky_user WITH PASSWORD 'strongpassword'; ALTER DATABASE bluesky_pds OWNER TO bluesky_user; GRANT ALL PRIVILEGES ON DATABASE bluesky_pds TO bluesky_user;
      CREATE DATABASE bluesky_pds; CREATE USER bluesky_user WITH PASSWORD 'strongpassword'; ALTER DATABASE bluesky_pds OWNER TO bluesky_user; GRANT ALL PRIVILEGES ON DATABASE bluesky_pds TO bluesky_user;
      CREATE DATABASE bluesky_pds; CREATE USER bluesky_user WITH PASSWORD 'strongpassword'; ALTER DATABASE bluesky_pds OWNER TO bluesky_user; GRANT ALL PRIVILEGES ON DATABASE bluesky_pds TO bluesky_user;

      Exit with:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      \q
      \q
      \q
    3. Update Database Configuration

      Edit the .env file again to match:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      nano .env
      nano .env
      nano .env

      Set:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      DB_HOST=localhost DB_PORT=5432 DB_USER=bluesky_user DB_PASSWORD=strongpassword DB_NAME=bluesky_pds
      DB_HOST=localhost DB_PORT=5432 DB_USER=bluesky_user DB_PASSWORD=strongpassword DB_NAME=bluesky_pds
      DB_HOST=localhost DB_PORT=5432 DB_USER=bluesky_user DB_PASSWORD=strongpassword DB_NAME=bluesky_pds
  7. Set Up Reverse Proxy with Nginx

    To expose your PDS with a proper domain and HTTPS, set up Nginx.

    1. Install Nginx

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      sudo apt install -y nginx
      sudo apt install -y nginx
      sudo apt install -y nginx
    2. Configure Nginx for Bluesky PDS

      Create a new configuration file:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      sudo nano /etc/nginx/sites-available/bluesky
      sudo nano /etc/nginx/sites-available/bluesky
      sudo nano /etc/nginx/sites-available/bluesky

      Paste the following:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      server { listen 80; server_name pds.yourdomain.com; location / { proxy_pass http://localhost:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
      server { listen 80; server_name pds.yourdomain.com; location / { proxy_pass http://localhost:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
      server { listen 80; server_name pds.yourdomain.com; location / { proxy_pass http://localhost:3000; 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; } }

      Save and exit.

    3. Enable the Configuration

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      sudo ln -s /etc/nginx/sites-available/bluesky /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl restart nginx
      sudo ln -s /etc/nginx/sites-available/bluesky /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl restart nginx
      sudo ln -s /etc/nginx/sites-available/bluesky /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl restart nginx
  8. Obtain an SSL Certificate

    To secure your instance with HTTPS, install Certbot for Let’s Encrypt:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d pds.yourdomain.com
    sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d pds.yourdomain.com
    sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d pds.yourdomain.com

    Follow the prompts and ensure HTTPS works.

  9. Start the Bluesky PDS

    Now, launch the server using Docker:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    docker-compose up -d
    docker-compose up -d
    docker-compose up -d

    This will download and start the Bluesky PDS.

    Check if the container is running:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    docker ps
    docker ps
    docker ps

    Check logs for errors:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    docker-compose logs -f
    docker-compose logs -f
    docker-compose logs -f
  10. Verify the PDS

    1. Check if Bluesky PDS is Running

      Test if the server is accessible:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      curl -X GET https://pds.yourdomain.com/xrpc/com.atproto.server.describeServer
      curl -X GET https://pds.yourdomain.com/xrpc/com.atproto.server.describeServer
      curl -X GET https://pds.yourdomain.com/xrpc/com.atproto.server.describeServer

      It should return server details.

    2. Register and Configure Your Server

      • Visit https://pds.yourdomain.com/admin
      • Log in with the admin password set in .env
      • Configure federation settings
  11. Maintain Your Server

    1. Restarting and Stopping PDS

      To restart:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      docker-compose restart
      docker-compose restart
      docker-compose restart

      To stop:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      docker-compose down
      docker-compose down
      docker-compose down
    2. Updating Bluesky PDS

      To update:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      cd pds git pull origin main docker-compose up --build -d
      cd pds git pull origin main docker-compose up --build -d
      cd pds git pull origin main docker-compose up --build -d
    3. Set Up Automatic SSL Renewal

      Let’s Encrypt certificates expire every 90 days. Set up auto-renewal:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      sudo crontab -e
      sudo crontab -e
      sudo crontab -e

      Add:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      0 3 * * * certbot renew --quiet
      0 3 * * * certbot renew --quiet
      0 3 * * * certbot renew --quiet
  12. Join Bluesky Federation

    To federate your instance:

    1. Register your PDS on Bluesky’s AT Protocol network.
    2. Ensure DNS records are correct for your domain.
    3. Monitor logs for federation activity.

Setup Custom Scripts for Personal Data Server Monitoring

To ensure your Bluesky Personal Data Server (PDS) is running smoothly, we can set up custom monitoring scripts using systemd, cron, and a simple health-check script that alerts via email or logs issues.

Follow the steps below to setup custom monitoring and alerting scripts:

  1. Create a Health Check Script

    We will write a script that:

    • Checks if the Bluesky PDS container is running.
    • Pings the PDS endpoint for a valid response.
    • Logs issues and sends an alert if the server is down.
    1. Create the Monitoring Script

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      sudo nano /opt/bluesky_pds_monitor.sh
      sudo nano /opt/bluesky_pds_monitor.sh
      sudo nano /opt/bluesky_pds_monitor.sh

      Paste the following script:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      #!/bin/bash # Variables PDS_URL="https://pds.yourdomain.com/xrpc/com.atproto.server.describeServer" LOG_FILE="/var/log/bluesky_pds_monitor.log" EMAIL="your-email@example.com" # Check if the Docker container is running CONTAINER_NAME="pds" if ! docker ps | grep -q "$CONTAINER_NAME"; then echo "$(date): PDS container is DOWN!" | tee -a "$LOG_FILE" echo "PDS container is DOWN on $(hostname)" | mail -s "Bluesky PDS Alert" $EMAIL docker-compose -f /home/bluesky/pds/docker-compose.yml restart exit 1 fi # Check if the PDS server responds RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" $PDS_URL) if [ "$RESPONSE" -ne 200 ]; then echo "$(date): PDS server is NOT RESPONDING ($RESPONSE)" | tee -a "$LOG_FILE" echo "Bluesky PDS is NOT RESPONDING ($RESPONSE) on $(hostname)" | mail -s "Bluesky PDS Alert" $EMAIL docker-compose -f /home/bluesky/pds/docker-compose.yml restart else echo "$(date): PDS is running fine." >> "$LOG_FILE" fi
      #!/bin/bash # Variables PDS_URL="https://pds.yourdomain.com/xrpc/com.atproto.server.describeServer" LOG_FILE="/var/log/bluesky_pds_monitor.log" EMAIL="your-email@example.com" # Check if the Docker container is running CONTAINER_NAME="pds" if ! docker ps | grep -q "$CONTAINER_NAME"; then echo "$(date): PDS container is DOWN!" | tee -a "$LOG_FILE" echo "PDS container is DOWN on $(hostname)" | mail -s "Bluesky PDS Alert" $EMAIL docker-compose -f /home/bluesky/pds/docker-compose.yml restart exit 1 fi # Check if the PDS server responds RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" $PDS_URL) if [ "$RESPONSE" -ne 200 ]; then echo "$(date): PDS server is NOT RESPONDING ($RESPONSE)" | tee -a "$LOG_FILE" echo "Bluesky PDS is NOT RESPONDING ($RESPONSE) on $(hostname)" | mail -s "Bluesky PDS Alert" $EMAIL docker-compose -f /home/bluesky/pds/docker-compose.yml restart else echo "$(date): PDS is running fine." >> "$LOG_FILE" fi
      #!/bin/bash # Variables PDS_URL="https://pds.yourdomain.com/xrpc/com.atproto.server.describeServer" LOG_FILE="/var/log/bluesky_pds_monitor.log" EMAIL="your-email@example.com" # Check if the Docker container is running CONTAINER_NAME="pds" if ! docker ps | grep -q "$CONTAINER_NAME"; then echo "$(date): PDS container is DOWN!" | tee -a "$LOG_FILE" echo "PDS container is DOWN on $(hostname)" | mail -s "Bluesky PDS Alert" $EMAIL docker-compose -f /home/bluesky/pds/docker-compose.yml restart exit 1 fi # Check if the PDS server responds RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" $PDS_URL) if [ "$RESPONSE" -ne 200 ]; then echo "$(date): PDS server is NOT RESPONDING ($RESPONSE)" | tee -a "$LOG_FILE" echo "Bluesky PDS is NOT RESPONDING ($RESPONSE) on $(hostname)" | mail -s "Bluesky PDS Alert" $EMAIL docker-compose -f /home/bluesky/pds/docker-compose.yml restart else echo "$(date): PDS is running fine." >> "$LOG_FILE" fi

      Save and exit (CTRL + X, then Y, then Enter).

  2. Make the Script Executable

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    sudo chmod +x /opt/bluesky_pds_monitor.sh
    sudo chmod +x /opt/bluesky_pds_monitor.sh
    sudo chmod +x /opt/bluesky_pds_monitor.sh
  3. Automate Monitoring Using Cron

    To run the script every 5 minutes, add it to cron:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    sudo crontab -e
    sudo crontab -e
    sudo crontab -e

    Add the following line:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    */5 * * * * /opt/bluesky_pds_monitor.sh
    */5 * * * * /opt/bluesky_pds_monitor.sh
    */5 * * * * /opt/bluesky_pds_monitor.sh

    Save and exit.

  4. Systemd Service for Persistent Monitoring

    Instead of relying only on cron, we can create a systemd service that ensures the PDS stays active.

    1. Create a Systemd Service File

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      sudo nano /etc/systemd/system/bluesky_pds.service
      sudo nano /etc/systemd/system/bluesky_pds.service
      sudo nano /etc/systemd/system/bluesky_pds.service

      Paste the following:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      [Unit] Description=Bluesky Personal Data Server After=network.target [Service] User=bluesky WorkingDirectory=/home/bluesky/pds ExecStart=/usr/bin/docker-compose up Restart=always RestartSec=10 KillMode=mixed StandardOutput=syslog StandardError=syslog SyslogIdentifier=bluesky_pds [Install] WantedBy=multi-user.target
      [Unit] Description=Bluesky Personal Data Server After=network.target [Service] User=bluesky WorkingDirectory=/home/bluesky/pds ExecStart=/usr/bin/docker-compose up Restart=always RestartSec=10 KillMode=mixed StandardOutput=syslog StandardError=syslog SyslogIdentifier=bluesky_pds [Install] WantedBy=multi-user.target
      [Unit] Description=Bluesky Personal Data Server After=network.target [Service] User=bluesky WorkingDirectory=/home/bluesky/pds ExecStart=/usr/bin/docker-compose up Restart=always RestartSec=10 KillMode=mixed StandardOutput=syslog StandardError=syslog SyslogIdentifier=bluesky_pds [Install] WantedBy=multi-user.target

      Save and exit.

  5. Enable and Start the Service

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    sudo systemctl daemon-reload sudo systemctl enable bluesky_pds sudo systemctl start bluesky_pds
    sudo systemctl daemon-reload sudo systemctl enable bluesky_pds sudo systemctl start bluesky_pds
    sudo systemctl daemon-reload sudo systemctl enable bluesky_pds sudo systemctl start bluesky_pds

    To check its status:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    sudo systemctl status bluesky_pds
    sudo systemctl status bluesky_pds
    sudo systemctl status bluesky_pds

    To restart it manually:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    sudo systemctl restart bluesky_pds
    sudo systemctl restart bluesky_pds
    sudo systemctl restart bluesky_pds
  6. Log Rotation for Monitoring Logs

    Since logs can grow large over time, we set up log rotation.

    Create a logrotate configuration file:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    sudo nano /etc/logrotate.d/bluesky_pds_monitor
    sudo nano /etc/logrotate.d/bluesky_pds_monitor
    sudo nano /etc/logrotate.d/bluesky_pds_monitor

    Add:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    /var/log/bluesky_pds_monitor.log { weekly rotate 4 compress missingok notifempty }
    /var/log/bluesky_pds_monitor.log { weekly rotate 4 compress missingok notifempty }
    /var/log/bluesky_pds_monitor.log { weekly rotate 4 compress missingok notifempty }

    Save and exit.

  7. Test the Monitoring Setup

    Run the monitoring script manually:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    sudo /opt/bluesky_pds_monitor.sh
    sudo /opt/bluesky_pds_monitor.sh
    sudo /opt/bluesky_pds_monitor.sh

    Check logs:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    cat /var/log/bluesky_pds_monitor.log
    cat /var/log/bluesky_pds_monitor.log
    cat /var/log/bluesky_pds_monitor.log

Overview of Monitoring Behavior

Now, your Bluesky PDS server is actively monitored. If the server crashes, it:

  1. Auto-restarts using docker-compose.
  2. Logs errors and keeps a history.
  3. Sends email alerts when issues occur.

Integrating Grafana Monitoring and Telegram Notifications for Bluesky PDS

To enhance monitoring for your Bluesky Personal Data Server (PDS), we will:

  1. Set up Prometheus for system and Docker metrics
  2. Use Grafana to visualize metrics
  3. Set up Telegram alerts for critical failures

To get started, please follow the steps provided:

  1. Install and Configure Prometheus

    1. Install Prometheus

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      sudo useradd --no-create-home --shell /bin/false prometheus sudo mkdir /etc/prometheus /var/lib/prometheus sudo chown prometheus:prometheus /etc/prometheus /var/lib/prometheus
      sudo useradd --no-create-home --shell /bin/false prometheus sudo mkdir /etc/prometheus /var/lib/prometheus sudo chown prometheus:prometheus /etc/prometheus /var/lib/prometheus
      sudo useradd --no-create-home --shell /bin/false prometheus sudo mkdir /etc/prometheus /var/lib/prometheus sudo chown prometheus:prometheus /etc/prometheus /var/lib/prometheus

      Download and install Prometheus:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      cd /tmp curl -LO "https://github.com/prometheus/prometheus/releases/latest/download/prometheus-$(uname -m)-linux-gnu.tar.gz" tar -xvzf prometheus-*-linux-gnu.tar.gz sudo mv prometheus-*/prometheus /usr/local/bin/ sudo mv prometheus-*/promtool /usr/local/bin/ sudo mv prometheus-*/consoles /etc/prometheus/ sudo mv prometheus-*/console_libraries /etc/prometheus/
      cd /tmp curl -LO "https://github.com/prometheus/prometheus/releases/latest/download/prometheus-$(uname -m)-linux-gnu.tar.gz" tar -xvzf prometheus-*-linux-gnu.tar.gz sudo mv prometheus-*/prometheus /usr/local/bin/ sudo mv prometheus-*/promtool /usr/local/bin/ sudo mv prometheus-*/consoles /etc/prometheus/ sudo mv prometheus-*/console_libraries /etc/prometheus/
      cd /tmp curl -LO "https://github.com/prometheus/prometheus/releases/latest/download/prometheus-$(uname -m)-linux-gnu.tar.gz" tar -xvzf prometheus-*-linux-gnu.tar.gz sudo mv prometheus-*/prometheus /usr/local/bin/ sudo mv prometheus-*/promtool /usr/local/bin/ sudo mv prometheus-*/consoles /etc/prometheus/ sudo mv prometheus-*/console_libraries /etc/prometheus/
    2. Configure Prometheus

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      sudo nano /etc/prometheus/prometheus.yml
      sudo nano /etc/prometheus/prometheus.yml
      sudo nano /etc/prometheus/prometheus.yml

      Paste the following:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      global: scrape_interval: 15s scrape_configs: - job_name: 'prometheus' static_configs: - targets: ['localhost:9090'] - job_name: 'docker' static_configs: - targets: ['localhost:9323']
      global: scrape_interval: 15s scrape_configs: - job_name: 'prometheus' static_configs: - targets: ['localhost:9090'] - job_name: 'docker' static_configs: - targets: ['localhost:9323']
      global: scrape_interval: 15s scrape_configs: - job_name: 'prometheus' static_configs: - targets: ['localhost:9090'] - job_name: 'docker' static_configs: - targets: ['localhost:9323']

      Save and exit.

    3. Create a Systemd Service for Prometheus

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      sudo nano /etc/systemd/system/prometheus.service
      sudo nano /etc/systemd/system/prometheus.service
      sudo nano /etc/systemd/system/prometheus.service

      Paste:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      [Unit] Description=Prometheus Monitoring Wants=network-online.target After=network-online.target [Service] User=prometheus ExecStart=/usr/local/bin/prometheus --config.file=/etc/prometheus/prometheus.yml --storage.tsdb.path=/var/lib/prometheus --web.listen-address="0.0.0.0:9090" Restart=always [Install] WantedBy=multi-user.target
      [Unit] Description=Prometheus Monitoring Wants=network-online.target After=network-online.target [Service] User=prometheus ExecStart=/usr/local/bin/prometheus --config.file=/etc/prometheus/prometheus.yml --storage.tsdb.path=/var/lib/prometheus --web.listen-address="0.0.0.0:9090" Restart=always [Install] WantedBy=multi-user.target
      [Unit] Description=Prometheus Monitoring Wants=network-online.target After=network-online.target [Service] User=prometheus ExecStart=/usr/local/bin/prometheus --config.file=/etc/prometheus/prometheus.yml --storage.tsdb.path=/var/lib/prometheus --web.listen-address="0.0.0.0:9090" Restart=always [Install] WantedBy=multi-user.target

      Enable and start Prometheus:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      sudo systemctl daemon-reload sudo systemctl enable prometheus sudo systemctl start prometheus
      sudo systemctl daemon-reload sudo systemctl enable prometheus sudo systemctl start prometheus
      sudo systemctl daemon-reload sudo systemctl enable prometheus sudo systemctl start prometheus
  2. Install and Configure Grafana

    1. Install Grafana

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      sudo apt install -y software-properties-common sudo add-apt-repository "deb https://packages.grafana.com/oss/deb stable main" sudo apt update && sudo apt install -y grafana
      sudo apt install -y software-properties-common sudo add-apt-repository "deb https://packages.grafana.com/oss/deb stable main" sudo apt update && sudo apt install -y grafana
      sudo apt install -y software-properties-common sudo add-apt-repository "deb https://packages.grafana.com/oss/deb stable main" sudo apt update && sudo apt install -y grafana

      Start Grafana:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      sudo systemctl enable --now grafana-server
      sudo systemctl enable --now grafana-server
      sudo systemctl enable --now grafana-server
    2. Access Grafana

      Visit http://your-server-ip:3000 and login with:

      • Username: admin
      • Password: admin

      Change the password after logging in.

  3. Add Prometheus Data Source to Grafana

    1. Go to GrafanaSettingsData Sources
    2. Click Add Data Source
    3. Choose Prometheus
    4. Set URL: http://localhost:9090
    5. Click Save & Test
  4. Install and Configure Node Exporter

    To monitor CPU, RAM, and disk usage:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    cd /tmp curl -LO "https://github.com/prometheus/node_exporter/releases/latest/download/node_exporter-$(uname -m)-linux-gnu.tar.gz" tar -xvzf node_exporter-*-linux-gnu.tar.gz sudo mv node_exporter-*/node_exporter /usr/local/bin/
    cd /tmp curl -LO "https://github.com/prometheus/node_exporter/releases/latest/download/node_exporter-$(uname -m)-linux-gnu.tar.gz" tar -xvzf node_exporter-*-linux-gnu.tar.gz sudo mv node_exporter-*/node_exporter /usr/local/bin/
    cd /tmp curl -LO "https://github.com/prometheus/node_exporter/releases/latest/download/node_exporter-$(uname -m)-linux-gnu.tar.gz" tar -xvzf node_exporter-*-linux-gnu.tar.gz sudo mv node_exporter-*/node_exporter /usr/local/bin/

    Create a service:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    sudo nano /etc/systemd/system/node_exporter.service
    sudo nano /etc/systemd/system/node_exporter.service
    sudo nano /etc/systemd/system/node_exporter.service

    Paste:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    [Unit] Description=Node Exporter After=network.target [Service] ExecStart=/usr/local/bin/node_exporter Restart=always User=root [Install] WantedBy=default.target
    [Unit] Description=Node Exporter After=network.target [Service] ExecStart=/usr/local/bin/node_exporter Restart=always User=root [Install] WantedBy=default.target
    [Unit] Description=Node Exporter After=network.target [Service] ExecStart=/usr/local/bin/node_exporter Restart=always User=root [Install] WantedBy=default.target

    Enable and start:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    sudo systemctl daemon-reload sudo systemctl enable node_exporter sudo systemctl start node_exporter
    sudo systemctl daemon-reload sudo systemctl enable node_exporter sudo systemctl start node_exporter
    sudo systemctl daemon-reload sudo systemctl enable node_exporter sudo systemctl start node_exporter
  5. Configure Telegram Alerts in Grafana

    1. Create a Telegram Bot

        1. Open Telegram and search for @BotFather
        2. Send /newbot
        3. Follow the instructions and get the Bot Token
        4. Add the bot to a Telegram group and get the Chat ID using:
          Plain text
          Copy to clipboard
          Open code in new window
          EnlighterJS 3 Syntax Highlighter
          curl "https://api.telegram.org/bot/getUpdates"
          curl "https://api.telegram.org/bot/getUpdates"
          curl "https://api.telegram.org/bot/getUpdates"
        5. Save the Bot Token and Chat ID.
  6. Configure Telegram in Grafana

    1. Go to GrafanaAlertingNotification Channels
    2. Click Add Channel
    3. Name: Telegram
    4. Type: Telegram
    5. Bot API Token:
    6. Chat ID:
    7. Click Save & Test
  7. Create Alerts for Bluesky PDS

    1. Add an Alert in Grafana

      1. Open Grafana and go to Dashboards.
      2. Create a new panel with:
        Plain text
        Copy to clipboard
        Open code in new window
        EnlighterJS 3 Syntax Highlighter
        query: up{job="node"}
        query: up{job="node"}
        query: up{job="node"}
      3. Click AlertCreate Alert Rule
      4. Set:
        • Condition: When value < 1
        • For: 1m
        • Notification Channel: Telegram
      5. Click Save & Apply
  8. Verify Monitoring & Alerts

    1. Restart Bluesky PDS and check Prometheus metrics:
      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      systemctl restart bluesky_pds
      systemctl restart bluesky_pds
      systemctl restart bluesky_pds
    2. Simulate a failure:
      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      docker stop pds
      docker stop pds
      docker stop pds
    3. Check if Grafana triggers a Telegram alert.

Summary of Grafana Monitors and Telegram Notifications

🎉 You now have Bluesky PDS monitoring with:
Prometheus for metrics
Grafana for dashboards
Telegram alerts for failures

Create Auto-Recovery Scripts for Bluesky PDS

To ensure Bluesky Personal Data Server (PDS) recovers automatically from failures, we will:

  • Monitor PDS health
  • Restart the service if down
  • Trigger Telegram alerts
  • Set up systemd and cron for self-healing
  1. Create the Auto-Recovery Script

    The script will:
    Check if the Docker container is running
    Restart the container if needed
    Log failures
    Send Telegram alerts

    1. Create the Script

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      sudo nano /opt/bluesky_auto_recovery.sh
      sudo nano /opt/bluesky_auto_recovery.sh
      sudo nano /opt/bluesky_auto_recovery.sh

      Paste the following:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      #!/bin/bash # Configuration PDS_CONTAINER="pds" PDS_URL="https://pds.yourdomain.com/xrpc/com.atproto.server.describeServer" LOG_FILE="/var/log/bluesky_auto_recovery.log" TELEGRAM_BOT_TOKEN="your_bot_token" TELEGRAM_CHAT_ID="your_chat_id" # Function to send Telegram alerts send_telegram_alert() { MESSAGE="$1" curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage" \ -d chat_id="$TELEGRAM_CHAT_ID" \ -d text="$MESSAGE" } # Check if Docker is running if ! systemctl is-active --quiet docker; then echo "$(date): Docker is DOWN. Restarting..." | tee -a "$LOG_FILE" send_telegram_alert "🚨 ALERT: Docker is DOWN on $(hostname). Restarting..." sudo systemctl restart docker fi # Check if the PDS container is running if ! docker ps | grep -q "$PDS_CONTAINER"; then echo "$(date): PDS container is DOWN! Restarting..." | tee -a "$LOG_FILE" send_telegram_alert "⚠️ WARNING: Bluesky PDS is DOWN! Restarting now..." docker-compose -f /home/bluesky/pds/docker-compose.yml up -d fi # Check if PDS is responding HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$PDS_URL") if [ "$HTTP_STATUS" -ne 200 ]; then echo "$(date): PDS is NOT RESPONDING ($HTTP_STATUS). Restarting..." | tee -a "$LOG_FILE" send_telegram_alert "❌ Bluesky PDS is NOT RESPONDING ($HTTP_STATUS). Restarting..." docker-compose -f /home/bluesky/pds/docker-compose.yml restart else echo "$(date): PDS is running fine." >> "$LOG_FILE" fi
      #!/bin/bash # Configuration PDS_CONTAINER="pds" PDS_URL="https://pds.yourdomain.com/xrpc/com.atproto.server.describeServer" LOG_FILE="/var/log/bluesky_auto_recovery.log" TELEGRAM_BOT_TOKEN="your_bot_token" TELEGRAM_CHAT_ID="your_chat_id" # Function to send Telegram alerts send_telegram_alert() { MESSAGE="$1" curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage" \ -d chat_id="$TELEGRAM_CHAT_ID" \ -d text="$MESSAGE" } # Check if Docker is running if ! systemctl is-active --quiet docker; then echo "$(date): Docker is DOWN. Restarting..." | tee -a "$LOG_FILE" send_telegram_alert "🚨 ALERT: Docker is DOWN on $(hostname). Restarting..." sudo systemctl restart docker fi # Check if the PDS container is running if ! docker ps | grep -q "$PDS_CONTAINER"; then echo "$(date): PDS container is DOWN! Restarting..." | tee -a "$LOG_FILE" send_telegram_alert "⚠️ WARNING: Bluesky PDS is DOWN! Restarting now..." docker-compose -f /home/bluesky/pds/docker-compose.yml up -d fi # Check if PDS is responding HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$PDS_URL") if [ "$HTTP_STATUS" -ne 200 ]; then echo "$(date): PDS is NOT RESPONDING ($HTTP_STATUS). Restarting..." | tee -a "$LOG_FILE" send_telegram_alert "❌ Bluesky PDS is NOT RESPONDING ($HTTP_STATUS). Restarting..." docker-compose -f /home/bluesky/pds/docker-compose.yml restart else echo "$(date): PDS is running fine." >> "$LOG_FILE" fi
      #!/bin/bash # Configuration PDS_CONTAINER="pds" PDS_URL="https://pds.yourdomain.com/xrpc/com.atproto.server.describeServer" LOG_FILE="/var/log/bluesky_auto_recovery.log" TELEGRAM_BOT_TOKEN="your_bot_token" TELEGRAM_CHAT_ID="your_chat_id" # Function to send Telegram alerts send_telegram_alert() { MESSAGE="$1" curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage" \ -d chat_id="$TELEGRAM_CHAT_ID" \ -d text="$MESSAGE" } # Check if Docker is running if ! systemctl is-active --quiet docker; then echo "$(date): Docker is DOWN. Restarting..." | tee -a "$LOG_FILE" send_telegram_alert "🚨 ALERT: Docker is DOWN on $(hostname). Restarting..." sudo systemctl restart docker fi # Check if the PDS container is running if ! docker ps | grep -q "$PDS_CONTAINER"; then echo "$(date): PDS container is DOWN! Restarting..." | tee -a "$LOG_FILE" send_telegram_alert "⚠️ WARNING: Bluesky PDS is DOWN! Restarting now..." docker-compose -f /home/bluesky/pds/docker-compose.yml up -d fi # Check if PDS is responding HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$PDS_URL") if [ "$HTTP_STATUS" -ne 200 ]; then echo "$(date): PDS is NOT RESPONDING ($HTTP_STATUS). Restarting..." | tee -a "$LOG_FILE" send_telegram_alert "❌ Bluesky PDS is NOT RESPONDING ($HTTP_STATUS). Restarting..." docker-compose -f /home/bluesky/pds/docker-compose.yml restart else echo "$(date): PDS is running fine." >> "$LOG_FILE" fi

      Save and exit (CTRL + X, then Y, then Enter).

  2. Make the Script Executable

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    sudo chmod +x /opt/bluesky_auto_recovery.sh
    sudo chmod +x /opt/bluesky_auto_recovery.sh
    sudo chmod +x /opt/bluesky_auto_recovery.sh
  3. Automate Recovery Using Cron

    To check every 5 minutes, add it to cron:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    sudo crontab -e
    sudo crontab -e
    sudo crontab -e

    Add:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    */5 * * * * /opt/bluesky_auto_recovery.sh
    */5 * * * * /opt/bluesky_auto_recovery.sh
    */5 * * * * /opt/bluesky_auto_recovery.sh

    Save and exit.

  4. Set Up Systemd for Automatic Restart

    Instead of relying only on cron, we use systemd to ensure PDS restarts if it crashes.

    1. Create a Systemd Service File

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      sudo nano /etc/systemd/system/bluesky_auto_recovery.service
      sudo nano /etc/systemd/system/bluesky_auto_recovery.service
      sudo nano /etc/systemd/system/bluesky_auto_recovery.service

      Paste:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      [Unit] Description=Bluesky PDS Auto-Recovery Service After=network.target docker.service [Service] ExecStart=/opt/bluesky_auto_recovery.sh Restart=always RestartSec=30 User=root [Install] WantedBy=multi-user.target
      [Unit] Description=Bluesky PDS Auto-Recovery Service After=network.target docker.service [Service] ExecStart=/opt/bluesky_auto_recovery.sh Restart=always RestartSec=30 User=root [Install] WantedBy=multi-user.target
      [Unit] Description=Bluesky PDS Auto-Recovery Service After=network.target docker.service [Service] ExecStart=/opt/bluesky_auto_recovery.sh Restart=always RestartSec=30 User=root [Install] WantedBy=multi-user.target

      Save and exit.

  5. Enable and Start the Service

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    sudo systemctl daemon-reload sudo systemctl enable bluesky_auto_recovery sudo systemctl start bluesky_auto_recovery
    sudo systemctl daemon-reload sudo systemctl enable bluesky_auto_recovery sudo systemctl start bluesky_auto_recovery
    sudo systemctl daemon-reload sudo systemctl enable bluesky_auto_recovery sudo systemctl start bluesky_auto_recovery

    To check its status:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    sudo systemctl status bluesky_auto_recovery
    sudo systemctl status bluesky_auto_recovery
    sudo systemctl status bluesky_auto_recovery

    To restart manually:

    Plain text
    Copy to clipboard
    Open code in new window
    EnlighterJS 3 Syntax Highlighter
    sudo systemctl restart bluesky_auto_recovery
    sudo systemctl restart bluesky_auto_recovery
    sudo systemctl restart bluesky_auto_recovery
  6. Test the Auto-Recovery System

    1. Simulate a Failure

      Stop the PDS container manually:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      docker stop pds
      docker stop pds
      docker stop pds

      Check logs:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      tail -f /var/log/bluesky_auto_recovery.log
      tail -f /var/log/bluesky_auto_recovery.log
      tail -f /var/log/bluesky_auto_recovery.log

      It should detect the failure, restart PDS, and send a Telegram alert.

    2. Simulate an Unresponsive PDS

      Block PDS temporarily:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      iptables -A INPUT -p tcp --dport 3000 -j DROP
      iptables -A INPUT -p tcp --dport 3000 -j DROP
      iptables -A INPUT -p tcp --dport 3000 -j DROP

      Wait 5 minutes and check logs & Telegram alerts.

      To unblock:

      Plain text
      Copy to clipboard
      Open code in new window
      EnlighterJS 3 Syntax Highlighter
      iptables -D INPUT -p tcp --dport 3000 -j DROP
      iptables -D INPUT -p tcp --dport 3000 -j DROP
      iptables -D INPUT -p tcp --dport 3000 -j DROP

Self-Healing Sequence Summary

Auto-recovery script to restart PDS
Telegram alerts for failures
Systemd service for persistent monitoring
Cron job for regular health checks

Now, your Bluesky PDS is self-healing! 🎉

Conclusion

Congratulations! 🎉 You’ve successfully self-hosted a Bluesky Personal Data Server on an Ubuntu VPS. Your server is now part of the decentralized Bluesky network.

Additionally, you’ve enabled:

  • Automated Service monitors via custom scripting
  • Grafana Dashboard monitoring and Telegram notifications
  • Self-healing: Automatic service recovery in case of failure.

Launch 100% ssd ubuntu vps from $2. 49/mo!

Resources

Further resources for managing your Bluesky personal data server:

Avatar of editorial staff

Editorial Staff

Rad Web Hosting is a leading provider of web hosting, Cloud VPS, and Dedicated Servers in Dallas, TX.

One thought on “Self-Hosting a Bluesky Personal Data Server on Ubuntu VPS

Comments are closed.

lg