Skip to content

Self-Hosted E2EE Pastebin - Paaster and Cloudflare to the Rescue!

Exploring how to self-host an end-to-end encrypted pastebin using Paaster and Cloudflare R2 storage. I'd like to easily share sensitive information like code snippets or logs with others without the need to trust a third-party service with capabilities like access code protection, expiration, view limits, etc.

Paaster

Paaster is a secure and user-friendly pastebin application that allows you to store and share text snippets. It features end-to-end encryption so that only people you share the link with can view the paste. It also supports access code protection, expiration, rate limiting, share via QR code, and more. There is support for multiple storage backends, including local file storage, AWS S3, Cloudflare R2, Google Cloud Storage, MinIO, Storj, and more. Easily deployable with Docker and even support for Vercel.

Cloudflare R2

Cloudflare R2 is a serverless edge storage platform that provides a simple, secure, and scalable way to store and serve data, similar to AWS S3 and other providers. Though this isn't quite "Self-Hosted", I'm pretty happy with it since the data is encrypted prior to being stored in the bucket, and only I have access to the encryption keys.

Guide

I primarily followed the Paaster Setup Instructions and started from the docker-public-s3/docker-compose.yml example. Paaster's frontend can be served with Vercel, but I opted to self-host both the frontend and backend, but with Cloudflare R2 as the storage backend.

  1. DNS Configuration.

    Choose a subdomain for your Paaster instance, e.g., paaster.example.com. Update the CNAME records to point to where you will host the frontend / backend. For example:

    • paaster.example.com - frontend
    • api-paaster.example.com - backend
  2. Cloudflare R2 Configuration.

    • Create a new R2 bucket in the Cloudflare dashboard. paaster
    • Enable Custom Domains for your bucket. Pick something like r2.paaster.example.com. This will need to be different from your frontend domain.
    • Configure Cross-Origin Resource Sharing (CORS) for your bucket. This is required for the frontend to interact with the R2 bucket.
      Allowed Origins: https://paaster.example.com
      Allowed Methods: GET
    • Retrieve the following from the R2 bucket settings, for example:
      • Location: Western North America (WNAM)
      • S3 API URL. This will be used in the backend configuration. https://***.r2.cloudflarestorage.com/paaster
    • Create a new Access Key and Secret Key for the R2 bucket, see R2 Authentication. These will be used in the backend configuration. paaster
      • Token Name: paaster
      • Permissions: Object Read & Write
      • Apply to specific buckets only: paaster
      • Note the Access Key and Secret Key.
  3. Backend & Frontend Configuration - Self-Hosted Docker-Compose.

    • Download the example docker-public-s3/docker-compose.yml file.

    • Update the backend:

      yml
      paaster_backend:
          ...
          environment:
              ...
              paaster_s3: >
                  '{
                      "endpoint_url": "<R2_S3_API_URL>", # https://***.r2.cloudflarestorage.com/paaster
                      "secret_access_key": "<R2_SECRET_ACCESS_KEY>",
                      "access_key_id": "<R2_ACCESS_KEY_ID>",
                      "region_name": "WNAM",
                      "bucket": "paaster",
                      "folder": "pastes",
                      "download_url": "https://r2.paaster.example.com"
                  }'
              paaster_proxy_urls: >
                  '{
                      "frontend": "https://paaster.example.com",
                      "backend": "http://api-paaster.example.com"
                  }'
    • Update the frontend:

      yml
      paaster_frontend:
          ...
          environment:
              ...
              VITE_NAME: "paaster.example.com"
              VITE_API_URL: "http://api-paaster.example.com"
    • Start the backend with docker-compose up -d.

    • Verify logs are healthy with docker-compose logs -f

  4. Reverse Proxy Configuration.

    Proxy exposed ports to the frontend and backend services using whatever reverse proxy you prefer, like Nginx, Traefik, Caddy, etc. For example, with nginx:

    nginx
    # Frontend
    server {
        listen 443 ssl http2;
        server_name paaster.*;
        client_max_body_size 0;
    
        location / {
            include /config/nginx/proxy.conf;
            include /config/nginx/resolver.conf;
            set $upstream_app <paaster_frontend_ip>;
            set $upstream_port 8889;
            set $upstream_proto http;
            proxy_pass $upstream_proto://$upstream_app:$upstream_port;
    
            # Security Headers
            add_header Strict-Transport-Security "max-age=31536000" always;
            add_header X-XSS-Protection "1; mode=block" always;
            add_header X-Frame-Options "DENY" always;
            add_header Feature-Policy "microphone 'none'; camera 'none'; geolocation 'none'; payment 'none'" always;
    
            # Remove the proxy_hide_header directive for X-Frame-Options if setting it explicitly
            proxy_hide_header X-Frame-Options;
        }
    }
    
    # Backend
    server {
        listen 443 ssl http2;
        server_name api-paaster.*;
        client_max_body_size 0;
    
        location / {
            include /config/nginx/proxy.conf;
            include /config/nginx/resolver.conf;
            set $upstream_app <paaster_backend_ip>;
            set $upstream_port 8888;
            set $upstream_proto http;
            proxy_pass $upstream_proto://$upstream_app:$upstream_port;
            proxy_hide_header X-Frame-Options;
        }
    }
  5. Try it Out!.

    • Visit your new https://paaster.example.com and create a new paste.
    • Share the paste with others securely!

Final Thoughts

Paaster is a fantastic self-hosted pastebin solution that provides end-to-end encrypted pastes that I can share with others securely. The setup was straightforward and I was able to get everything up and running. I'm excited to start using Paaster to share sensitive information with others securely!

Deployed on Deno 🦕