How to Self-Host Piped with Docker Compose

What Is Piped?

Piped is a privacy-focused alternative frontend for YouTube. It proxies all requests through your server so Google never sees your IP address, strips tracking, removes ads, and integrates SponsorBlock to skip sponsor segments automatically. Piped supports user accounts, subscriptions, playlists, and watch history — all stored locally on your instance.

Unlike Invidious (which uses scraping), Piped uses YouTube’s internal API with a proxy layer for IP rotation. The result is faster video loading and better compatibility with YouTube’s frequent changes.

Prerequisites

  • A Linux server (Ubuntu 22.04+ recommended)
  • Docker and Docker Compose installed (guide)
  • 2 GB of RAM minimum (4 GB recommended)
  • A domain name with 3 subdomains configured (required for HTTPS)
  • Ports 80 and 443 open

Docker Compose Configuration

Piped requires 6 services: frontend, backend, proxy, background helper, PostgreSQL, and a reverse proxy. The official deployment uses an internal Nginx router with Caddy for TLS termination.

The recommended approach uses the official Piped-Docker repository:

git clone https://github.com/TeamPiped/Piped-Docker
cd Piped-Docker
./configure-instance.sh

The script prompts for your three subdomains and generates all configuration files. If you prefer manual setup, here’s the full stack:

Create a docker-compose.yml file:

services:
  piped-frontend:
    image: 1337kavin/piped-frontend:latest  # Piped does not publish semver tags — :latest is the only option
    restart: unless-stopped
    environment:
      BACKEND_HOSTNAME: pipedapi.example.com
      PROXY_HOSTNAME: pipedproxy.example.com
    depends_on:
      - piped
    networks:
      - piped

  piped:
    image: 1337kavin/piped:latest  # Piped does not publish semver tags — :latest is the only option
    restart: unless-stopped
    volumes:
      - ./config/config.properties:/app/config.properties:ro
    depends_on:
      - postgres
    networks:
      - piped

  piped-proxy:
    image: 1337kavin/piped-proxy:latest  # Piped does not publish semver tags — :latest is the only option
    restart: unless-stopped
    volumes:
      - piped-proxy:/app/socket
    networks:
      - piped

  bg-helper:
    image: 1337kavin/bg-helper:latest  # Piped does not publish semver tags — :latest is the only option
    restart: unless-stopped
    networks:
      - piped

  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    volumes:
      - piped-db:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: piped
      POSTGRES_USER: piped
      POSTGRES_PASSWORD: changeme_use_a_strong_password
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U piped"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - piped

  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./config/Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy-data:/data
      - piped-proxy:/var/run/ytproxy
    depends_on:
      - piped
      - piped-frontend
      - piped-proxy
    networks:
      - piped

volumes:
  piped-db:
  piped-proxy:
  caddy-data:

networks:
  piped:
    driver: bridge

Create config/config.properties for the backend:

# Public URLs — change these to your actual domains
API_URL=https://pipedapi.example.com
FRONTEND_URL=https://piped.example.com
PROXY_PART=https://pipedproxy.example.com

# Server
PORT=8080
HTTP_WORKERS=2

# Database (Hibernate)
hibernate.connection.url=jdbc:postgresql://postgres:5432/piped
hibernate.connection.username=piped
hibernate.connection.password=changeme_use_a_strong_password

# Features
DISABLE_REGISTRATION=false
FEED_RETENTION=30
DISABLE_LBRY=false

# SponsorBlock
SPONSORBLOCK_SERVERS=https://sponsor.ajay.app

Create config/Caddyfile for Caddy:

piped.example.com {
    reverse_proxy piped-frontend:80
}

pipedapi.example.com {
    reverse_proxy piped:8080
}

pipedproxy.example.com {
    @ytproxy path /videoplayback* /api/v4/* /api/manifest/*
    reverse_proxy @ytproxy unix//var/run/ytproxy/http-proxy.sock

    header / {
        Access-Control-Allow-Origin *
        Access-Control-Allow-Methods "GET, POST, OPTIONS"
    }

    reverse_proxy piped-proxy:8080
}

Replace all instances of example.com with your actual domain. Create DNS A records for all three subdomains pointing to your server.

Start the stack:

docker compose up -d

Initial Setup

  1. Wait for Caddy to obtain SSL certificates. This takes 1-2 minutes after DNS propagates. Check with docker compose logs caddy.
  2. Access the frontend at https://piped.example.com.
  3. Create an account by clicking Register. Your account data stays on your instance — Google never sees it.
  4. Import YouTube subscriptions: Export your subscriptions from Google Takeout as a JSON file, then import via Piped’s settings.

Configuration

Disable Public Registration

After creating your account, disable registration to keep your instance private:

# In config/config.properties
DISABLE_REGISTRATION=true

Restart the backend: docker compose restart piped

Feed Retention

Control how long subscription feed data is kept:

# Days to retain feed items (default: 30)
FEED_RETENTION=30

Return YouTube Dislike Integration

Enable dislike counts using the Return YouTube Dislike API:

DISABLE_RYD=false
RYD_PROXY_URL=https://returnyoutubedislikeapi.com

Advanced Configuration (Optional)

IPv6 Proxy Rotation

YouTube bans IPs that make too many requests. The piped-proxy service handles basic proxying, but for high-traffic instances you’ll want IPv6 rotation. Configure your VPS with a /48 or /64 IPv6 block and set up the proxy to rotate source addresses.

Federation

Piped supports instance federation. To list your instance publicly and share subscriptions across the Piped network, see the Piped Federation documentation.

Reverse Proxy

The Docker Compose above includes Caddy for automatic HTTPS. If you’re already running a reverse proxy like Nginx Proxy Manager or Traefik, remove the Caddy service and configure your existing proxy to forward traffic to the appropriate internal services.

For Nginx Proxy Manager, create three proxy hosts pointing to the Docker network:

  • piped.example.compiped-frontend:80
  • pipedapi.example.compiped:8080
  • pipedproxy.example.compiped-proxy:8080

See Reverse Proxy Setup for detailed instructions.

Backup

Back up the PostgreSQL database volume. This contains all user accounts, subscriptions, and playlists:

docker compose exec postgres pg_dump -U piped piped > piped-backup.sql

Restore:

cat piped-backup.sql | docker compose exec -T postgres psql -U piped piped

See Backup Strategy for automated backup approaches.

Troubleshooting

Videos Won’t Load (PoToken Errors)

Symptom: “Could not get stream” or signatureTimestamp errors. Fix: Ensure the bg-helper service is running. This service generates proof-of-origin tokens that YouTube requires. Check: docker compose logs bg-helper

SSL Certificate Errors

Symptom: Caddy logs show “ACME challenge failed” or similar. Fix: Ensure all three DNS A records are pointing to your server’s IP and have propagated (use dig piped.example.com). Caddy cannot obtain certificates until DNS is configured correctly.

YouTube IP Ban (Slow or Failing Playback)

Symptom: Videos buffer endlessly or fail intermittently. Fix: Your server’s IP is being rate-limited by YouTube. Consider IPv6 rotation or using a residential proxy. Single-IP instances will eventually get throttled under heavy use.

Database Connection Refused

Symptom: Backend logs show “Connection refused” to PostgreSQL. Fix: Wait for the PostgreSQL health check to pass before the backend starts. If persistent, check that POSTGRES_PASSWORD matches in both the Compose file and config.properties.

Frontend Shows “API Unreachable”

Symptom: Frontend loads but shows no content. Fix: Verify BACKEND_HOSTNAME in the frontend environment matches your actual API subdomain. Check that the API service is accessible: curl -s https://pipedapi.example.com/healthcheck

Resource Requirements

  • RAM: 700 MB idle (backend is Java-based), 1.5 GB under active use
  • CPU: 2 cores recommended for smooth video proxying
  • Disk: 500 MB for application data, plus PostgreSQL storage (grows slowly with user data)

Verdict

Piped is the best privacy-focused YouTube frontend for self-hosting. It’s faster than Invidious, handles YouTube’s API changes better thanks to the PoToken system, and includes SponsorBlock integration. The three-subdomain requirement adds setup complexity, but the official configure-instance.sh script makes deployment straightforward.

Use Piped if you want ad-free, tracking-free YouTube on your own infrastructure. For a simpler but less full-featured alternative, consider Invidious. If you want to archive YouTube content rather than stream it, look at Tube Archivist.

Frequently Asked Questions

How does Piped compare to Invidious?

Both are privacy-focused YouTube frontends, but they use different technical approaches. Piped uses YouTube’s internal API with a proxy layer and PoToken system for authentication. Invidious uses scraping. Piped generally handles YouTube’s frequent changes better and loads videos faster. Invidious has a simpler single-container setup. Choose Piped for better performance and compatibility. Choose Invidious for simpler deployment.

Why does Piped need three subdomains?

Piped separates the frontend, API, and video proxy into three services, each needing its own subdomain for CORS and SSL to work correctly. Typically: piped.example.com (frontend), pipedapi.example.com (API), and pipedproxy.example.com (video proxy). The official configure-instance.sh script generates all the configuration files for your three subdomains automatically.

Will YouTube block my Piped instance?

Eventually, yes — single-IP instances get rate-limited under heavy use. YouTube throttles IPs that make many requests. Mitigation options include IPv6 rotation, residential proxies, or limiting the number of concurrent users. Light personal use on a single instance typically works fine. For shared instances with many users, IP rotation is essential.

Can I import my YouTube subscriptions into Piped?

Yes. Piped supports importing subscriptions from YouTube (via Google Takeout), Invidious, and NewPipe. Export your subscription list as an OPML or JSON file and import it through the Piped settings page. Subscriptions, playlists, and watch history are stored locally on your instance.

How much RAM does Piped use?

The backend is Java-based, so expect 700 MB idle and 1.5 GB under active use. PostgreSQL adds another 200–300 MB. The proxy service uses minimal RAM. Total: plan for 2 GB minimum, 4 GB recommended for smooth operation with multiple concurrent users streaming video.

Does Piped support SponsorBlock?

Yes — SponsorBlock integration is built in. Piped automatically skips sponsor segments, intros, outros, and other community-flagged sections in videos. You can configure which segment types to skip in the settings. This works out of the box with no additional setup.

Comments