[Docker] PeerTube on Synology NAS
![[Docker] PeerTube on Synology NAS](/content/images/size/w1200/2025/09/peertube.jpg)
YouTube is one of the world’s largest video streaming platforms. For many people, it’s not just a place to share content but also a source of livelihood.
But what if you could run your own version of YouTube on a personal server?
Someone thought of that — and that’s how PeerTube was born: a self-hosted, federated video platform.
Since PeerTube comes with an official Docker image, I decided to give it a try on my Synology NAS. Spoiler: it wasn’t as smooth as I hoped.
First Attempts
I started the installation with a light heart, thinking it would be a weekend side project.
But PeerTube has a lot of environment variables, and if you miss just one, you’ll be greeted with the dreaded:
“Build failed”
So, instead of over-customizing everything on the first run, I aimed for the simplest possible configuration that would at least launch successfully.
Folder Structure
PeerTube stores data in multiple places (database, Redis, app config, and uploaded videos).
On Synology, it’s a good idea to create dedicated folders under /volume1/docker/peertube
so you can map them as persistent volumes.
/volume1/docker/peertube/
├── docker-compose.yml # Project root file (rundocker compose up
here)
├── .env # Environment variables (can also be named stack.env)
├── redis/ # Redis persistent data
├── db/ # PostgreSQL persistent data
├── data/ # PeerTube videos and uploaded media
└── config/ # PeerTube configuration files
Reverse Proxy Setup
I used Synology’s built-in Reverse Proxy (Control Panel → Login Portal → Advanced → Reverse Proxy).
Required settings:
- Target: Forward requests from
tube.yourdomain.com
to the PeerTube container (localhost:9500
in my case). - WebSocket: Enable WebSocket support. This automatically adds the required
Upgrade
andConnection
headers. - Custom Headers:
X-Forwarded-For = $remote_addr
→ Passes the real client IPX-Forwarded-Proto = $scheme
→ Marks all traffic as HTTPS (since the proxy terminates TLS)X-Real-IP = $remote_addr
→ Some apps prefer this
Without these headers, PeerTube won’t log the right IPs or may not recognize secure connections properly.
docker-compose.yml
Here’s my docker-compose.yml
. I’ve added comments so you can follow what’s happening:
services:
# ── Redis for caching and background jobs ───────────────
redis:
image: redis:7
command: ["redis-server","--requirepass","${REDIS_PASSWORD}"]
container_name: peertube-redis
hostname: peertube-redis
security_opt:
- no-new-privileges:true
healthcheck:
# Ensure Redis is alive by pinging it with a password
test: ["CMD-SHELL", "redis-cli -h 127.0.0.1 -p 6379 -a \"$REDIS_PASSWORD\" ping | grep -q PONG"]
interval: 15s
timeout: 5s
retries: 10
start_period: 10s
volumes:
- /volume1/docker/peertube/redis:/data:rw
env_file: ./.env
restart: unless-stopped
networks:
- peertube-net
# ── PostgreSQL database for PeerTube ────────────────────
db:
image: postgres:16
container_name: peertube-db
hostname: peertube-db
security_opt:
- no-new-privileges:true
healthcheck:
# Check PostgreSQL readiness with pg_isready
test: ["CMD-SHELL","pg_isready -q -d \"$POSTGRES_DB\" -U \"$POSTGRES_USER\""]
timeout: 60s
interval: 15s
retries: 10
env_file: ./.env
volumes:
- /volume1/docker/peertube/db:/var/lib/postgresql/data:rw
restart: unless-stopped
networks:
- peertube-net
# ── The PeerTube application itself ─────────────────────
peertube:
image: chocobozzz/peertube:v7.2.3-bookworm
container_name: peertube
hostname: peertube
security_opt:
- no-new-privileges:true
# Optional: enable hardware transcoding on Intel-based NAS
devices:
- /dev/dri:/dev/dri
env_file: ./.env
environment:
TZ: Asia/Seoul
# Logging verbosity
PEERTUBE_LOG_LEVEL: info
# Hardware transcoding options (optional)
PEERTUBE_TRANSCODING_HWACCEL: vaapi
PEERTUBE_TRANSCODING_FFMPEG_ARGS__GLOBAL: '["-vaapi_device","/dev/dri/renderD128"]'
PEERTUBE_TRANSCODING_FFMPEG_ARGS__H264: '["-c:v","h264_vaapi","-bf","0"]'
healthcheck:
# PeerTube healthcheck: API ping
test: ["CMD-SHELL","curl -fsS http://localhost:9000/api/v1/ping || exit 1"]
interval: 30s
timeout: 5s
retries: 10
stop_grace_period: 60s
ports:
- 1934:1935 # RTMP live streaming
- 9500:9000 # Web UI, proxied by Synology RP
volumes:
- /volume1/docker/peertube/data:/data:rw
- /volume1/docker/peertube/config:/config:rw
restart: unless-stopped
depends_on:
redis:
condition: service_healthy
db:
condition: service_healthy
networks:
- peertube-net
networks:
peertube-net:
driver: bridge
ipam:
config:
- subnet: 172.59.0.0/16
.env
Here’s the environment file. I assumed Gmail as the SMTP provider:
# ── Domain and email settings ─────────────────────────────
PEERTUBE_WEBSERVER_HOSTNAME=tube.yourdomain.com
# Always quote email addresses
PEERTUBE_ADMIN_EMAIL="your email"
PEERTUBE_SMTP_USERNAME=your email
PEERTUBE_SMTP_PASSWORD=your-google-app-password # Use a Google APP Password (16 digits)
PEERTUBE_SMTP_FROM=your email
PEERTUBE_SMTP_HOSTNAME=smtp.gmail.com
# ── Random 32-character secret ────────────────────────────
# Change this value to your own generated secret
PEERTUBE_SECRET=2TcW4FHZmEF8l501xrbKGovXE5OzhTCe
# ── Database and Redis settings ───────────────────────────
POSTGRES_USER=peertube
POSTGRES_PASSWORD=peertube
POSTGRES_DB=peertube
REDIS_PASSWORD=redispass
# ── Webserver config ─────────────────────────────────────
PEERTUBE_WEBSERVER_USE_HTTP=false
PEERTUBE_WEBSERVER_TRUST_PROXY=true
# ── Mail config ──────────────────────────────────────────
PEERTUBE_SMTP_PORT=465
PEERTUBE_SMTP_TLS=true
PEERTUBE_SMTP_DISABLE_STARTTLS=false
# ── Internal service links ───────────────────────────────
PEERTUBE_DB_NAME=$POSTGRES_DB
PEERTUBE_DB_USERNAME=$POSTGRES_USER
PEERTUBE_DB_PASSWORD=$POSTGRES_PASSWORD
PEERTUBE_DB_SSL=false
PEERTUBE_DB_HOSTNAME=peertube-db
PEERTUBE_REDIS_HOSTNAME=peertube-redis
PEERTUBE_REDIS_AUTH=$REDIS_PASSWORD
# ── Trust proxy IP ranges ────────────────────────────────
PEERTUBE_TRUST_PROXY=["127.0.0.1", "loopback", "172.59.0.0/16"]
# ── Optional: object storage ACLs ─────────────────────────
PEERTUBE_OBJECT_STORAGE_UPLOAD_ACL_PUBLIC="public-read"
PEERTUBE_OBJECT_STORAGE_UPLOAD_ACL_PRIVATE="private"
# ── Logging ──────────────────────────────────────────────
PEERTUBE_LOG_LEVEL=info

First Login
After building and starting the project in Synology’s Container Manager, the first screen should appear.
However, before logging in as the root user, you’ll need to set a password:
# Open a terminal inside the peertube container
docker exec -it peertube bash
(or you may use Container Manager to open Terminal)# Run the password reset command
npm run reset-password -- -u root
You’ll be prompted to enter a new password. If you seeUser password updated.
you’re ready to log in at https://tube.yourdomain.com
.
Impressions
And there it was: my very own personal YouTube.
Well, not exactly “YouTuber” but more like a PeerTuber 😅
PeerTube even has a mobile app, and it can connect to not just your own server but also other PeerTube instances around the world. It’s meant to form a federated network, but in practice, it’s still a bit niche — and you often need to sign up on each server individually.
For me, it was mostly the fun of having a personal video archive. On a small scale, it could also work well for a private team to share videos.
But running it on a Synology NAS isn’t easy. Uploading and transcoding videos is heavy, and my NAS struggled. Since I’m already running multiple Docker services (including this blog), system resources were stretched thin, and performance of other apps suffered.
Still, the experiment was worth it. I now have my own video storage service, and it was a fun project to try out.