For development, you'll need:
- Node.js 20.19+ and npm (needed for the build script that compiles the client and installs dependencies before Docker runs)
- Docker and Docker Compose (v2 or v1)
- Git for version control
- A code editor (VS Code recommended)
Note: Runtime dependencies (MariaDB, yt-dlp, ffmpeg, Node) run inside the dev containers, but the build-dev script uses your host Node.js to install packages and build the frontend before the Docker image is created.
Youtarr/
├── client/ # React frontend
│ ├── src/
│ │ ├── components/ # React components
│ │ ├── types/ # TypeScript definitions
│ │ └── providers/ # Context providers
│ └── package.json
├── server/ # Node.js backend
│ ├── models/ # Sequelize models
│ ├── modules/ # Business logic
│ ├── db.js # Database connection
│ └── server.js # Express server
├── migrations/ # Database migrations
├── config/ # Configuration files
├── scripts/ # Utility scripts
├── docker-compose.external-db.yml # Config that does not start internal DB (dev & prod)
└── docker-compose.yml # Docker Compose configuration with internal db (dev & prod)
git clone https://github.com/DialmasterOrg/Youtarr.git
cd Youtarr# Install root dependencies
npm i
# First build (installs dependencies and builds the client)
./scripts/build-dev.sh --install-deps
# Subsequent builds (after code changes)
./scripts/build-dev.shOptional flags:
--install-deps- Runsnpm installin the root andclient/before building (required on a clean clone or after dependency changes)--no-cache- Force rebuild to get latest yt-dlp versionSKIP_DEV_IMAGE_PRUNE=1- Skip automatic cleanup of old untaggedyoutarr-devimages (pruning is enabled by default to keep Docker storage from filling)
The script runs npm run build for the client and then invokes docker build, so make sure Node.js 20.19+ and npm are available locally.
- Copy
.env.exampleto.env- This is optional, if not created manually it will be created by the
./scripts/start-dev.shscript - Edit
.envto configure your YOUTUBE_OUTPUT_DIR- This will be the directory that is mounted by Youtarr where downloaded videos will be placed
- It defaults to
./downloads
- Leave
AUTH_PRESET_USERNAMEandAUTH_PRESET_PASSWORDblank to configure your login via UI on first startup (credentials will be saved toconfig/config.json)
- This is optional, if not created manually it will be created by the
Step 1: Full Docker Development
Build and run the full stack using the pre-built static frontend served by the app container.
# Start both app and database containers (serves static frontend)
./scripts/start-dev.shThis starts:
- Backend on http://localhost:3011 (Node.js Express server with
--watchfor auto-restart) - Frontend (static, served by the app container) on http://localhost:3087
- MariaDB database on port 3321
Optional flags:
--no-auth- Disable authentication (only use behind auth gateway or if not exposed outside your network)--debug- Set logging level to "debug" (defaults toinfo)
Step 2: Vite Dev Server (Hot Module Reload — optional)
For faster, iterative frontend development you can run the Vite dev server with HMR. This is optional; run it when you want instant frontend reloads while developing UI.
# Terminal 1: Start backend in Docker
./scripts/start-dev.sh
# Terminal 2: Start Vite dev server (HMR)
cd client
npm run devThen access:
- Frontend (HMR) at http://localhost:3000
The Vite dev server will proxy API and WebSocket requests to the backend at port 3011 so API calls work the same as the full-stack run.
Use Storybook to develop and document components in isolation.
Prerequisites: Complete Step 2 (Build Development Environment) first, or at minimum install client dependencies:
cd client && npm installThis automatically generates the MSW (Mock Service Worker) file needed for API mocking in stories.
# Start Storybook Server
npm run storybookStorybook will open automatically in your browser. Story validation is done via Jest tests (see client/src/tests/storybook_coverage.test.js).
Navigate to:
- Docker static build: http://localhost:3087
- Vite dev server (if running): http://localhost:3000
Create your admin account on first access.
./stop.sh!IMPORTANT!: This COMPLETELY resets your local environment and REMOVES all data except your downloaded videos!
DATABASE AND CONFIGURATION IS REMOVED AND NOT BACKED UP!
This can be useful for local development and testing
./scripts/reset-server-data.shThe development setup is a "build-and-test-in-Docker" workflow that ensures your code works in a containerized environment:
Build Phase (./scripts/build-dev.sh):
- Runs
npm installon your host (if--install-depsis used) - Builds the React frontend (
npm run buildin client directory) - creates production build - Builds a Docker image with the pre-built static files
Runtime Phase (./scripts/start-dev.sh):
- Runs the Node.js Express server with
node server/server.js(no hot reload) - Serves the pre-built React static files from
/app/client/build - Application accessible at http://localhost:3087
The development setup mounts these directories only:
volumes:
- ./server/images:/app/server/images # Generated thumbnails
- ./config:/app/config # Configuration files
- ./jobs:/app/jobs # Job stateImportant: Source code (client/src/, server/*.js) is NOT mounted. This means:
- ❌ No hot reload or live reload
- ❌ Code changes are NOT automatically reflected
- ✅ You must rebuild after every code change
You must rebuild (./scripts/build-dev.sh) for:
- Any frontend code changes (React components, styles, etc.)
- Any backend code changes (server.js, modules, etc.)
- Installing new npm dependencies (use
--install-depsflag) - Updating system dependencies (yt-dlp, ffmpeg - use
--no-cacheflag) - Changing Dockerfile
- First time setup
- ✅ Tests your code in the actual Docker environment it will run in
- ✅ Catches Docker-specific issues early
- ✅ Consistent behavior between development and production
- ✅ Database runs in Docker (no local MariaDB installation needed)
- ✅ Includes all system dependencies (yt-dlp, ffmpeg)
Vite Dev Server Workflow (Recommended for Frontend)
# Terminal 1: Start backend
./scripts/start-dev.sh
# Terminal 2: Start Vite with HMR
cd client
npm run dev
# Make code changes - frontend updates instantly!
# Backend changes auto-restart via --watch
# View logs
docker compose -f docker-compose.dev.yml logs -f youtarr
# Stop when done
./stop.shFull Docker Workflow (For Testing Production Build)
# Start development environment
./scripts/start-dev.sh
# Make code changes in your editor
# After making changes, rebuild and restart:
./scripts/build-dev.sh
./scripts/start-dev.sh # Automatically stops and restarts containers
# View logs
docker compose -f docker-compose.dev.yml logs -f
# Stop when done
./stop.shNote: Youtarr only supports the Docker-based workflow described here. Always build and test inside the dev containers rather than trying to run the backend or frontend directly on the host.
# View running containers
docker compose ps
# Execute commands in app container
docker compose exec youtarr bash
# Execute commands in database container
docker compose exec youtarr-db bash
# Restart a specific service
docker compose restart youtarr
# View logs for specific service
docker compose logs -f youtarr
docker compose logs -f youtarr-db# From host
mysql -h localhost -P 3321 -u root -p123qweasd youtarr
# From inside container
docker compose exec youtarr-db mysql -u root -p123qweasd youtarrThe project uses ESLint for code quality. Configuration is in .eslintrc.js.
# Lint all code
npm run lint
# Lint with auto-fix
npm run lint:fix
# Lint specific areas
npm run lint:frontend
npm run lint:backend
# TypeScript type checking
npm run lint:tsHusky is configured to run linting, typescript checks and tests before commits. To bypass (not recommended):
git commit --no-verifyAll tests run on your host machine (not in Docker) since they're isolated unit tests:
# Run all tests (backend + frontend)
npm test
# Backend tests only
npm run test:backend
# Frontend tests only
npm run test:frontend
# Run with coverage
npm run test:coverage
# Watch mode (backend)
npm run test:watchcd client
npm test # Watch mode
npm test -- --coverage # With coverage
npm test -- --watchAll=false # Run once# Create a new migration
./scripts/db-create-migration.sh migration-name
# Migrations run automatically on container startupSee DATABASE.md
Option 1: Container Debugging with VS Code
Create .vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "attach",
"name": "Docker: Attach to Node",
"remoteRoot": "/usr/src/app",
"localRoot": "${workspaceFolder}",
"protocol": "inspector",
"port": 9229,
"restart": true,
"sourceMaps": true
}
]
}Modify docker-compose.yml to expose debug port:
ports:
- "3011:3011"
- "9229:9229" # Debug port
command: node --inspect=0.0.0.0:9229 server/server.jsOption 2: Console Debugging
console.log('Debug info:', variable);
debugger; // BreakpointOption 3: Exec into Container
docker compose exec youtarr bash
# Now you can run commands, inspect files, etc.- React Developer Tools browser extension
- Network tab for API calls
- Console for errors and logs
- Source maps are included in the production build for debugging
Enable Sequelize logging in db.js:
const sequelize = new Sequelize({
// ... other config
logging: console.log // Enable SQL logging
});Youtarr provides interactive API documentation via Swagger UI:
- Swagger UI: http://localhost:3087/swagger
- OpenAPI JSON: http://localhost:3087/swagger.json
The Swagger documentation includes:
- All available endpoints with request/response schemas
- Authentication requirements
- Try-it-out functionality for testing endpoints
- Request body examples and parameter descriptions
Routes are organized into modular files under ./server/routes/:
server/routes/
├── auth.js # Authentication endpoints
├── channels.js # Channel management
├── config.js # Configuration endpoints
├── health.js # Health check endpoints
├── jobs.js # Download job management
├── plex.js # Plex integration
├── setup.js # Initial setup endpoints
├── videos.js # Video management
└── index.js # Route registration
All endpoints require authentication except setup and health checks:
- Setup:
GET /setup/status,POST /setup/create-auth - Auth:
POST /auth/login,POST /auth/logout,GET /auth/sessions - Config:
GET /getconfig,POST /updateconfig - Channels:
GET /getchannels,POST /updatechannels - Downloads:
POST /triggerspecificdownloads,GET /jobstatus/:jobId - Plex:
GET /getplexlibraries,GET /plex/auth-url - Health:
GET /api/health,GET /api/db-status
For the complete list of 40+ endpoints, see the Swagger documentation.
All API endpoints (except setup, login, and health checks) require authentication:
headers: {
'x-access-token': 'session-token-here'
}When adding new API endpoints:
- Add the route to the appropriate file in
./server/routes/ - Include JSDoc annotations for Swagger documentation:
/**
* @swagger
* /api/your-endpoint:
* get:
* summary: Brief description
* description: Detailed description of what the endpoint does.
* tags: [CategoryTag]
* parameters:
* - in: query
* name: paramName
* schema:
* type: string
* description: Parameter description
* responses:
* 200:
* description: Success response description
*/
router.get('/api/your-endpoint', verifyToken, async (req, res) => {
// Implementation
});- The Swagger documentation will automatically update on the next server restart
WebSocket shares the HTTP port (3011 in container, 3087 on host) and emits:
downloadProgress- Download progress updatesdownloadComplete- Video download finishedchannelsUpdated- Channel list changed
Youtarr uses a dev → main branching model:
| Branch | Purpose | Docker Tag |
|---|---|---|
main |
Stable, released code | latest, vX.X.X |
dev |
Integration branch for upcoming release | dev-latest, dev-rc.<sha> |
feature/*, fix/* |
Individual changes | None |
-
Start from dev branch:
git checkout dev git pull origin dev git checkout -b feat/your-feature
-
Make changes and commit:
git add . git commit -m "feat: add new feature"
-
Push and create pull request targeting
dev(notmain) -
After merge to
dev, an RC Docker image is automatically built -
When ready, maintainer creates PR from
dev→mainfor production release
Follow conventional commits for automatic versioning:
feat:- New feature (minor version bump)fix:- Bug fix (patch version bump)docs:- Documentation onlystyle:- Code style changesrefactor:- Code refactoringtest:- Test changeschore:- Build process or auxiliary tool changesBREAKING CHANGE:- Breaking change (major version bump)
Before submitting PR:
- PR targets
devbranch - Code passes linting (
npm run lint) - All tests pass (
npm test) - Database migrations included (if needed)
- Documentation updated
- No sensitive data in commits
- Follows existing code style
# Build local image
./scripts/build-dev.sh
# Test locally
./scripts/start-dev.shReleases are automated via GitHub Actions with a two-stage workflow:
Release Candidates (automatic on dev merge):
- Merge your PR to
devbranch - RC workflow automatically:
- Builds multi-arch Docker images (amd64 + arm64)
- Pushes
dev-latestanddev-rc.<sha>tags to Docker Hub
Production Releases (automatic on main merge):
- Maintainer creates PR from
dev→main - After merge, release workflow automatically:
- Bumps version based on commit messages
- Updates CHANGELOG.md
- Creates GitHub release
- Builds optimized Docker image (~600MB)
- Pushes
latestandvX.X.Xtags to Docker Hub
# Check container status
docker compose ps
# View logs
docker compose logs
# Rebuild from scratch
docker compose down -v
./scripts/build-dev.sh --no-cache
./scripts/start-dev.sh# Find process using port
lsof -i :3011 # Mac/Linux
netstat -ano | findstr :3011 # Windows
# Or stop all Docker containers
docker compose down-
Check database is running:
docker compose ps youtarr-db
-
Check database logs:
docker compose logs youtarr-db
-
Test connection:
mysql -h localhost -P 3321 -u root -p123qweasd
Code changes always require a rebuild since source code is not mounted in the container:
# Rebuild and restart (start-dev.sh automatically stops first)
./scripts/build-dev.sh
./scripts/start-dev.shNote: This is expected behavior - the development setup builds the code into the image, it doesn't use live file mounting for source code.
# Rebuild with fresh dependencies
./scripts/build-dev.sh --install-deps- Never commit
.envfiles - Use environment variables for secrets
- Sanitize user inputs
- Validate all API inputs
- Use prepared statements for SQL (Sequelize handles this)
- Keep dependencies updated
# Check for vulnerabilities
npm audit
# Fix vulnerabilities
npm audit fix
# Check frontend
cd client && npm auditAdd to server.js for request timing:
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
console.log(`${req.method} ${req.path} - ${Date.now() - start}ms`);
});
next();
});Use React DevTools Profiler to identify performance bottlenecks.