Self-Hosting PlayVideo
Deploy PlayVideo on your own infrastructure. Same API, your servers, zero vendor lock-in.
Why Self-Host?
- Data sovereignty - Keep videos on your infrastructure
- Cost control - Use your own storage (S3, R2, MinIO)
- Customization - Modify the code to fit your needs
- No vendor lock-in - Switch between self-hosted and cloud anytime
Requirements
- Node.js 18+
- PostgreSQL 14+
- FFmpeg
- Storage backend (local, S3, R2, or MinIO)
- (Optional) Redis for async processing
Quick Start with Docker
# Clone the repository
git clone https://github.com/vascogaspar/blockbuster.git
cd playvideo
# Copy environment file
cp .env.example .env
# Start with Docker Compose
docker compose up -d
Access at http://localhost:8184
Environment Variables
# Required
DATABASE_URL=postgresql://user:pass@localhost:5432/playvideo
SESSION_SECRET=your-random-secret-at-least-32-chars
# Storage (choose one)
STORAGE_PROVIDER=local # or 's3' or 'r2'
# For S3/R2
S3_BUCKET=your-bucket
S3_REGION=us-east-1
S3_ACCESS_KEY=xxx
S3_SECRET_KEY=xxx
S3_ENDPOINT=https://xxx.r2.cloudflarestorage.com # For R2
# Optional - Async processing
REDIS_URL=redis://localhost:6379
# Optional - OAuth (for dashboard login)
GOOGLE_CLIENT_ID=xxx
GOOGLE_CLIENT_SECRET=xxx
GITHUB_CLIENT_ID=xxx
GITHUB_CLIENT_SECRET=xxx
# Optional - Billing
STRIPE_SECRET_KEY=xxx
STRIPE_WEBHOOK_SECRET=xxx
Storage Configuration
Local Storage (Development)
STORAGE_PROVIDER=local
LOCAL_STORAGE_PATH=./storage
Files stored in ./storage directory, served via /storage endpoint.
Cloudflare R2 (Recommended)
STORAGE_PROVIDER=r2
S3_BUCKET=your-bucket
S3_REGION=auto
S3_ACCESS_KEY=xxx
S3_SECRET_KEY=xxx
S3_ENDPOINT=https://xxx.r2.cloudflarestorage.com
PUBLIC_STORAGE_URL=https://your-bucket.yourdomain.com
R2 offers free egress, making it ideal for video delivery.
AWS S3
STORAGE_PROVIDER=s3
S3_BUCKET=your-bucket
S3_REGION=us-east-1
S3_ACCESS_KEY=xxx
S3_SECRET_KEY=xxx
PUBLIC_STORAGE_URL=https://your-bucket.s3.amazonaws.com
MinIO (Self-Hosted S3)
STORAGE_PROVIDER=s3
S3_BUCKET=videos
S3_REGION=us-east-1
S3_ACCESS_KEY=minioadmin
S3_SECRET_KEY=minioadmin
S3_ENDPOINT=http://localhost:9000
PUBLIC_STORAGE_URL=http://localhost:9000/videos
Deployment Options
Railway (One-Click)
Render
- Fork the repository
- Connect to Render
- Create a new Web Service from your fork
- Add environment variables
- Deploy
Docker Compose (Production)
version: '3.8'
services:
app:
image: playvideo/playvideo:latest
ports:
- "8184:8184"
environment:
DATABASE_URL: postgresql://postgres:postgres@db:5432/playvideo
REDIS_URL: redis://redis:6379
STORAGE_PROVIDER: r2
# ... other env vars
depends_on:
- db
- redis
db:
image: postgres:15
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_DB: playvideo
POSTGRES_PASSWORD: postgres
redis:
image: redis:7-alpine
volumes:
pgdata:
Kubernetes
Helm chart coming soon. For now, use the Docker image with your own manifests.
Database Setup
# Run migrations
npx prisma migrate deploy
# (Optional) Seed with demo data
npx prisma db seed
FFmpeg Installation
FFmpeg is required for video transcoding.
Ubuntu/Debian
apt-get update && apt-get install -y ffmpeg
macOS
brew install ffmpeg
Docker
The official Docker image includes FFmpeg.
Scaling
Horizontal Scaling
Run multiple app instances behind a load balancer. Requires:
- Shared storage (S3/R2, not local)
- Redis for job queue coordination
Worker Separation
Run API and workers separately:
# API server
ENABLE_WORKERS=false node dist/index.js
# Worker process
node dist/worker.js
Monitoring
Health Check
curl http://localhost:8184/health
Returns:
{
"status": "healthy",
"timestamp": "2026-01-07T12:00:00.000Z",
"version": "1.0.0"
}
Logs
Logs are written to stdout in JSON format. Use your preferred log aggregator.
Upgrading
# Pull latest
git pull origin main
# Install dependencies
npm install
# Run migrations
npx prisma migrate deploy
# Rebuild
npm run build
# Restart
pm2 restart playvideo
Using the SDK with Self-Hosted
Point the SDK to your instance:
import PlayVideo from '@playvideo/playvideo-sdk';
const bb = new PlayVideo({
apiKey: 'your-api-key',
baseUrl: 'https://video.yourdomain.com'
});
// Use exactly like the hosted version
const video = await bb.videos.upload({
file: './video.mp4',
collection: 'my-app'
});