YouTube Converter V1
A complete deployment guide for installing YouTube Converter V1 on a Linux VPS, configuring the media-processing stack, running it as a production service, and optionally exposing it through Nginx and HTTPS.
1. Requirements
Recommended VPS
- Ubuntu 24.04 LTS or current Debian
- 2 CPU cores minimum
- 2 GB RAM minimum; 4 GB+ recommended
- 20 GB+ free disk space
- Root or sudo access
- Public IP if internet-facing
V1 tested software
| Component | Version |
|---|---|
| Node.js | v22.23.2 |
| npm | 10.9.8 |
| yt-dlp | 2026.07.04 |
| FFmpeg | 6.1.1 |
| FFprobe | 6.1.1 |
2. Prepare the VPS
Connect over SSH and update the server:
ssh root@YOUR_SERVER_IP
apt update
apt upgrade -y
apt install -y curl git build-essential ca-certificates python3 python3-venv python3-pip nginx ufw
cat /etc/os-release
uname -a
df -h3. Install Node.js
The V1 release was tested with Node.js 22.23.2.
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
apt install -y nodejs
node --version
npm --versionFor the tested environment, the versions were v22.23.2 and 10.9.8. Repository packages can move forward, so pin versions if you need a reproducible environment.
4. Install FFmpeg and FFprobe
apt install -y ffmpeg
command -v ffmpeg
command -v ffprobe
ffmpeg -version | head -1
ffprobe -version | head -15. Download the application
cd /opt
git clone git@github.com:Tankonline/youtube-converter-v1.git youtube-converter-v1
cd /opt/youtube-converter-v1
git status
git log --oneline --decorate -3
npm ci
npm list --depth=0If GitHub SSH authentication is not configured on the VPS, clone the repository using its HTTPS URL instead.
6. Create and configure the application
cd /opt/youtube-converter-v1
cp config/config.example.yml config/config.yml
mkdir -p data temp
chmod 640 config/config.ymlServer
For a reverse-proxy deployment, keep Node private:
server:
host: "127.0.0.1"
port: 3000Tools
tools:
ytdlp: "./venv/bin/yt-dlp"
ffmpeg: "ffmpeg"
ffprobe: "ffprobe"Review the conversion limits, queue limits, rate limits, retention settings and branding before exposing the service publicly.
7. Install the tested yt-dlp version
The virtual environment is intentionally created on each VPS; it is not part of the Git repository.
cd /opt/youtube-converter-v1
python3 -m venv venv
./venv/bin/pip install --upgrade pip
./venv/bin/pip install yt-dlp==2026.07.04
./venv/bin/yt-dlp --versionThen ensure config/config.yml contains:
ytdlp: "./venv/bin/yt-dlp"node -e "const { loadConfig } = require('./config/config'); console.log(loadConfig().tools)"8. Test before production
Validate configuration
node -e "const { loadConfig } = require('./config/config'); console.log(loadConfig())"Start manually
npm startFrom another SSH session:
curl -I http://127.0.0.1:3000/
curl -s http://127.0.0.1:3000/ | grep -o '<title[^>]*>[^<]*</title>'Test yt-dlp separately
./venv/bin/yt-dlp --verbose --skip-download \
"https://www.youtube.com/watch?v=VIDEO_ID"Test the API
curl -sS -X POST http://127.0.0.1:3000/api/info \
-H 'Content-Type: application/json' \
--data '{"url":"https://www.youtube.com/watch?v=VIDEO_ID"}'9. Production systemd service
Use systemd instead of relying on an SSH session or nohup.
useradd --system --home /opt/youtube-converter-v1 --shell /usr/sbin/nologin youtubeconverter || true
chown -R youtubeconverter:youtubeconverter /opt/youtube-converter-v1
nano /etc/systemd/system/youtube-converter.serviceService file:
[Unit]
Description=YouTube Converter V1
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=youtubeconverter
Group=youtubeconverter
WorkingDirectory=/opt/youtube-converter-v1
Environment=NODE_ENV=production
ExecStart=/usr/bin/npm start
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
[Install]
WantedBy=multi-user.targetsystemctl daemon-reload
systemctl enable --now youtube-converter
systemctl status youtube-converter --no-pager
journalctl -u youtube-converter -n 100 --no-pager10. Nginx reverse proxy
Replace converter.example.com with your real domain.
nano /etc/nginx/sites-available/youtube-converterserver {
listen 80;
listen [::]:80;
server_name converter.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600;
proxy_send_timeout 3600;
}
}ln -s /etc/nginx/sites-available/youtube-converter /etc/nginx/sites-enabled/youtube-converter
nginx -t
systemctl reload nginx11. HTTPS
Point your DNS A/AAAA records at the VPS first, then:
apt install -y certbot python3-certbot-nginx
certbot --nginx -d converter.example.com
certbot renew --dry-run12. Security hardening
Firewall
ufw allow OpenSSH
ufw allow 'Nginx Full'
ufw enable
ufw status verbose- Keep Node bound to 127.0.0.1.
- Do not publicly expose port 3000.
- Prefer SSH keys over passwords.
- Keep the OS and application dependencies patched.
- Run the production service as a dedicated non-root account.
- Keep runtime data and private configuration out of Git.
- Monitor disk usage.
13. Updating the deployment
cd /opt/youtube-converter-v1
cp config/config.yml /root/youtube-converter-config.backup.yml
git status
git pull --ff-only
npm ci
./venv/bin/pip install --upgrade yt-dlp
systemctl restart youtube-converter
systemctl status youtube-converter --no-pager
journalctl -u youtube-converter -n 100 --no-pagerVerify deployed versions
git log --oneline --decorate -5
git status
node --version
npm --version
./venv/bin/yt-dlp --version
ffmpeg -version | head -1
ffprobe -version | head -114. Troubleshooting
Service will not start
systemctl status youtube-converter --no-pager
journalctl -u youtube-converter -n 200 --no-pager
cd /opt/youtube-converter-v1
node -e "const { loadConfig } = require('./config/config'); console.log(loadConfig())"Port conflict
ss -ltnp | grep ':3000'
ps aux | grep '[n]ode.*youtube-converter'/api/info fails
cd /opt/youtube-converter-v1
./venv/bin/yt-dlp --version
./venv/bin/yt-dlp --verbose --skip-download "https://www.youtube.com/watch?v=VIDEO_ID"Use the direct yt-dlp error to identify YouTube availability, authentication, bot challenge, JavaScript-runtime, or extractor issues.
Nginx returns 502
systemctl status youtube-converter --no-pager
ss -ltnp | grep ':3000'
curl -I http://127.0.0.1:3000/
nginx -t
journalctl -u nginx -n 100 --no-pagerStorage problems
df -h
du -sh /opt/youtube-converter-v1/temp
du -sh /opt/youtube-converter-v1/dataPermission problems
namei -l /opt/youtube-converter-v1
ls -ld /opt/youtube-converter-v1/data /opt/youtube-converter-v1/temp
ps aux | grep '[n]ode.*server.js'15. Uninstall
systemctl disable --now youtube-converter
rm -f /etc/systemd/system/youtube-converter.service
systemctl daemon-reload
rm -f /etc/nginx/sites-enabled/youtube-converter
rm -f /etc/nginx/sites-available/youtube-converter
nginx -t
systemctl reload nginx
# Only run after confirming all required data has been backed up:
rm -rf /opt/youtube-converter-v116. Final deployment checklist
- VPS updated and secured
- Node.js/npm installed
- FFmpeg/FFprobe installed
- Repository cloned and npm ci completed
- config/config.yml created
- data and temp directories created
- Python virtual environment created
- yt-dlp 2026.07.04 installed/tested
- Local HTTP endpoint returns 200
- Direct yt-dlp extraction tested
- systemd service enabled
- Nginx configured if public
- HTTPS configured if public
- Firewall configured
- Port 3000 remains private
- Production service runs as non-root
- Disk and conversion limits reviewed
