Portainer Templates logo

Portainer Templates

MeTube MeTube

Container

Downloaders

Web GUI for youtube-dl (using the yt-dlp fork) with playlist support. Allows you to download videos from YouTube and dozens of other sites (https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md)

Image details

Pulls: 49.2M
Architecture: amd64, arm64
Image size: 304 MB
Latest: 2026.09.29
User: alexta69
Created: Dec 03, 2019
Updated: 14 hours ago
Status: active

Source details

Stars: 194k
Forks: 16.9k
Language: Python
License: Unlicense
Updated: 2 days ago

Configuration

Type
Container
Platform
linux
Image
alexta69/metube:latest
Ports
8081:8081/tcp
Volumes
/downloads : /portainer/Downloads
Env vars
PUID=1000PGID=1000
Restart
unless-stopped

Notes

Template created by Pi-Hosted Series
Check our Github page: https://github.com/pi-hosted/pi-hosted

Official Webpage: https://hub.docker.com/r/alexta69/metube
Official Docker Documentation: https://github.com/alexta69/metube


Standalone Install

Select an install method, to see config/commands for deploying MeTube

Installation method

Install on Portainer

Import all app templates into your Portainer instance, for easy 1-click deploys

  1. Ensure both Docker and Portainer are installed, and up-to-date
  2. Log into your Portainer web UI
  3. Under Settings → App Templates, paste the below URL
  4. Head to Home → App Templates, and the list of apps will show up
  5. Select MeTube, fill in any config options, and hit Deploy

Template Import URL

https://raw.githubusercontent.com/Lissy93/portainer-templates/main/templates.json
Show Me demo

More install options in our documentation, or see yt-dlp/yt-dlp for app-specific guidance.

MeTube

Build Status Docker Pulls
MeTube is a self-hosted web UI for yt-dlp. Paste a link from YouTube or hundreds of other sites, and it downloads to your server:
  • Video, audio, subtitles and thumbnails — from single videos, whole playlists or entire channels.
  • Subscriptions to channels and playlists, which queue new uploads as they appear.
  • Per-download choices of quality, format and folder, plus server-wide yt-dlp options and presets.

📖 Guides, recipes and a troubleshooting FAQ live in the wiki — many feature requests are already a recipe there, so check it before filing one.
screenshot1

Quick start

docker run -d -p 8081:8081 -v /path/to/downloads:/downloads ghcr.io/alexta69/metube

Or with Docker Compose:
services:
  metube:
    image: ghcr.io/alexta69/metube
    container_name: metube
    restart: unless-stopped
    ports:
      - "8081:8081"
    volumes:
      - /path/to/downloads:/downloads

Then open http://<host>:8081 in your browser. Images are multi-arch (amd64/arm64), and also published on Docker Hub as alexta69/metube.

Configuration

MeTube is configured with environment variables: -e NAME=value on the docker run command line, or the environment: section in Compose. Defaults are the Docker image's; outside Docker, the directories default to the working directory.

Runtime and permissions

VariableDefaultDescription
PUID / PGID1000User and group MeTube runs as and writes files with. Legacy UID/GID also work.
UMASK022Umask for the files MeTube creates.
CHOWNDIRStrueMake PUID:PGID the owner of the download, state and temp directories at startup. With false, MeTube's user must already have access.
LOGLEVELINFODEBUG, INFO, WARNING, ERROR, CRITICAL or NONE.
ENABLEACCESSLOGfalseLog every HTTP request.
DEFAULTTHEMEautoUI theme: light, dark, or auto to follow the system.

Downloads

VariableDefaultDescription
MAXCONCURRENTDOWNLOADS3Downloads that run at once; the rest wait their turn.
DEFAULTOPTIONPLAYLISTITEMLIMIT0Default for the Items Limit field: how many entries of a playlist or channel to download (0 = all).
CLEARCOMPLETEDAFTER0Seconds before finished and failed downloads leave the Completed list (0 = never).
DELETEFILEONTRASHCANfalseAlso delete the file from disk when its entry is removed from Completed. ask prompts each time.
SUBSCRIPTIONDEFAULTCHECKINTERVAL60Default minutes between checks of a subscription.
SUBSCRIPTIONSCANPLAYLISTEND50Newest entries fetched each time a subscription is checked.
SUBSCRIPTIONMAXSEENIDS50000Video IDs remembered per subscription, to bound the state file's size.

Directories

VariableDefaultDescription
DOWNLOADDIR/downloadsWhere downloads are saved.
AUDIODOWNLOADDIRsame as DOWNLOADDIRWhere audio-only downloads are saved, to keep them apart from video.
TEMPDIR/downloadsWhere files are written while downloading. An SSD or tmpfs is faster, but on a RAM disk interrupted downloads can't resume.
STATEDIR/downloads/.metubeWhere MeTube keeps its queue, history, subscriptions and uploaded cookies.
CUSTOMDIRStrueShow a Download Folder field under Advanced Options, to save into a subfolder of the download directory.
CREATECUSTOMDIRStrueLet that field create folders that don't exist yet.
CUSTOMDIRSEXCLUDEREGEX(^\</td><td>/)<a href="undefined">.@</a>.<em>$Folders left out of the field's suggestions; the default hides names starting with . or @. Empty hides none.
DEFAULTFOLDERFolder the field starts with, relative to the download directory. Requires CUSTOMDIRS.
DOWNLOADDIRSINDEXABLEfalseServe browsable listings of the download directories.

File naming

Templates use yt-dlp's output template syntax
. How MeTube applies them is explained in Output templates.
VariableDefaultDescription
OUTPUTTEMPLATE%(title)s.%(ext)sFilename for downloads.
OUTPUTTEMPLATEPLAYLIST%(playlisttitle)s/%(title)s.%(ext)sFilename for items added from a playlist. Empty means OUTPUTTEMPLATE.
OUTPUTTEMPLATECHANNEL%(channel)s/%(title)s.%(ext)sFilename for items added from a channel. Empty means OUTPUTTEMPLATE.
OUTPUTTEMPLATECHAPTER%(title)s - %(sectionnumber)02d - %(sectiontitle)s.%(ext)sDefault filename for each chapter when Split by chapters is on.

yt-dlp

VariableDefaultDescription
YTDLOPTIONS{}Options for every download, as a JSON object — see yt-dlp options.
YTDLOPTIONSFILEPath to a JSON file of such options. Reloaded when it changes; wins over YTDLOPTIONS for the same key.
YTDLOPTIONSPRESETS{}Named option bundles, picked per download in the UI.
YTDLOPTIONSPRESETSFILEPath to a JSON file of presets. Reloaded when it changes; wins over YTDLOPTIONSPRESETS for the same name.
ALLOWYTDLOPTIONSOVERRIDESfalseShow a field for per-download yt-dlp options in the UI. Trusted users only: it allows running commands in the container.
YTDLNIGHTLYUPDATETIMEUse nightly yt-dlp builds, upgrading and restarting daily at this time (HH:MM, 24-hour).

Web server and network

VariableDefaultDescription
HOST0.0.0.0Address to listen on. The default is every IPv4 interface; (or empty) adds IPv6; :: is IPv6 only.
PORT8081Port to listen on.
URLPREFIX/Base path, for serving MeTube under a subpath of a reverse proxy.
HTTPSfalseServe HTTPS directly, using CERTFILE and KEYFILE.
CERTFILE / KEYFILEPaths to the HTTPS certificate and key.
PUBLICHOSTURLdownload/Base URL for the links to completed files. The default is relative to MeTube itself; set a full URL if the files are served from elsewhere.
PUBLICHOSTAUDIOURLaudiodownload/The same, for audio downloads.
CORSALLOWEDORIGINSComma-separated origins allowed to call MeTube from other sites, as browser extensions and bookmarklets do. allows any site, so prefer naming them.
ALLOWPRIVATEADDRESSESfalseAllow URLs on private and internal addresses, turning off SSRF protection. Needed for Fake-IP proxy clients, not for ordinary proxies — details.
ROBOTSTXTPath to a robots.txt file to serve, mounted into the container.

yt-dlp options

yt-dlp options are JSON objects keyed by yt-dlp's API option names, which differ from its command-line flags (converter). They apply at three levels, and for an option set at more than one, the more specific level wins:
  1. Global — YTDL_OPTIONS and YTDL_OPTIONS_FILE, for every download.
  2. Presets — YTDL_OPTIONS_PRESETS and YTDL_OPTIONS_PRESETS_FILE, picked per download under Advanced Options.
  3. Overrides — typed into the UI for a single download, when ALLOW_YTDL_OPTIONS_OVERRIDES is on.

environment:
  - 'YTDL_OPTIONS={"writesubtitles": true, "subtitleslangs": ["en", "de"], "updatetime": false}'

The yt-dlp options guide covers presets, overrides and option files in detail, and the YTDLOPTIONS Cookbook has ready-made recipes.

Guides

The wiki covers everything beyond the reference above:

Keeping yt-dlp up to date

Sites change constantly, and yt-dlp keeps up with frequent releases. A new MeTube image is published automatically for every yt-dlp stable release, so keep your container updated — watchtower can do it for you. To follow yt-dlp's nightly builds instead, set YTDL_NIGHTLY_UPDATE_TIME.

Troubleshooting and support

MeTube is only a UI for yt-dlp, so most download failures are yt-dlp's. Reproduce them with yt-dlp inside the container, and once a set of options works there, carry it over to YTDL_OPTIONS:
docker exec -ti metube sh
cd /downloads
yt-dlp <url>

The Troubleshooting FAQ covers common problems. Ask questions in Discussions; issues are for bugs in MeTube itself.

Scope and contributing

MeTube's scope is deliberately narrow: it downloads well, and stops once the file is written. Tagging and library organization belong to dedicated tools — point beets (headless, scriptable), MusicBrainz Picard (GUI, acoustic fingerprinting) or Lidarr (full library manager) at your AUDIO_DOWNLOAD_DIR.
Feature requests that fit this scope are welcome, and so are pull requests — for a change that isn't obvious, start a discussion first. CONTRIBUTING.md has the scope policy and how to build and run MeTube locally.

Serve MeTube on your own domain behind Caddy, Nginx or Traefik. Fill in your domain and copy the result. It's a starting point, some apps need their own base URL or extra headers set too.

Proxying metube.example.com to http://MeTube:8081

Add this to your Caddyfile

metube.example.com {
	reverse_proxy http://MeTube:8081
}

Check the logs first

Nine times out of ten the logs tell you exactly what went wrong.

  • In Portainer, go to Containers, click the container, then Logs. Or run docker logs MeTube
  • Exit codes help too: 137 means killed, usually out of memory. 126 or 127 means the command inside the image is broken.

Port already in use

If deployment fails with "Bind for 0.0.0.0:8081 failed: port is already allocated", something else on your server is using that port.

  • Find what's using it: sudo ss -tlnp | grep :8081
  • Stop the other service, or pick a different host port. In 8081:8081 only the left number is yours to change, the right one belongs to the app.

Running but the page won't load

The container is up but nothing appears in your browser.

  • Use your server's real IP: http://your-server-ip:8081. The 0.0.0.0 link Portainer shows isn't a real address.
  • Give it a minute after first deploy, MeTube can take a while to initialise.
  • Make sure your firewall allows the port, e.g. sudo ufw allow 8081

Permission denied on volumes

If the logs show "permission denied", the app can't write to its data folder on the host.

  • Fix the ownership: sudo chown -R 1000:1000 /portainer/Downloads
  • Or set the PUID and PGID variables (defaults 1000:1000) to match your own user, found with id $USER

Image won't pull

Test the pull directly on the host: docker pull alexta69/metube:latest

  • "manifest unknown" means the tag no longer exists. This template uses latest, so try pinning a specific version instead.
  • "toomanyrequests" is the Docker Hub rate limit. Log in with docker login to raise it.
  • "no space left on device" means a full disk. Reclaim space with docker system prune

"exec format error"

This means the image was built for a different CPU architecture than your server.

  • This image supports: amd64, arm64
  • Check yours with uname -m: x86_64 is amd64, aarch64 is arm64. Raspberry Pi and other ARM boards are the usual culprits.

Container keeps restarting

The unless-stopped restart policy relaunches the app after every crash, so the real error can scroll past.

  • Check the logs right after a restart, the last few lines before it died are the useful ones.
  • Get the exit code with docker inspect MeTube --format '{{.State.ExitCode}}'
  • Still stuck? Redeploy once with the restart policy set to no so the failure stays visible.

Raise an issue

Found something which isn't working as it should? Here's how to report it.

A single container

MeTube runs as one container, the simplest kind of app here. Just the one image to pull and nothing else wired up alongside it.

The app image

An image is the app packed up ready to go, everything MeTube needs bundled into one download. This template pulls alexta69/metube:latest, which Docker fetches once (about 304 MB) and then starts your own copy from.

Where the image comes from

Docker pulls its images from registries, public libraries of ready-built apps. MeTube's comes from Docker Hub, published by alexta69.

Version tags

The bit after the colon in the image name is the version tag. Here it's latest, which always points at the newest build, so a redeploy can bump you to a newer release without you asking. Newest right now is 2026.09.29. Pin a specific tag if you would rather stay on one version.

Which machines it runs on

Every image is built for particular CPU types. This one ships for amd64, arm64, so it runs on both regular x86 servers and ARM boards like a Raspberry Pi.

Ports

A port is the door the app answers on. A mapping like 8081:8081 means it's reachable on port 8081 of your server, where the left number is yours to change and the right one belongs to the app. It opens:

  • 8081:8081

Volumes

A volume is where MeTube keeps its files so they survive an update or a restart. Without one, anything it saves would sit inside the container and vanish the moment it's recreated. This template mounts:

  • /downloads from /portainer/Downloads on the host

Environment variables

Environment variables are the settings you hand over when you deploy, things like a password or a timezone. MeTube takes 2 of them, all with defaults you can leave alone or tweak:

  • PUID, defaults to 1000
  • PGID, defaults to 1000

Restart policy

The restart policy here is unless-stopped, so Docker restarts MeTube after a crash or reboot, but leaves it off when you stop it on purpose. You can change this on the deploy screen. The choices are no (never restart), on-failure (only after a crash), unless-stopped (restart unless you stop it), and always (bring it back no matter what).

Users and permissions

The PUID and PGID settings tell it which user and group to act as on your host. Point them at your own account (find yours with id $USER) so the files it writes into your mounted folders come out owned by you rather than root.

Networking

Nothing custom is set, so MeTube sits on Docker's default bridge network: its own private space that reaches the outside world only through the ports it publishes.

Container name

Once it's deployed, Portainer names the container MeTube. That's what you'll spot in the containers list and use in commands like docker logs MeTube.

Platform

The platform is linux, the kind of system the container is built to run on. Docker and Portainer handle this on a normal Linux server.

Open source license

MeTube is open source, released under the Unlicense license. In plain terms the code is out in the open, so you're free to run it and change it to fit what you need.

Portainer app templates

Zooming out, this whole page comes from a Portainer app template: a short recipe telling Portainer how to set MeTube up. Add the template list to Portainer once, then deploying MeTube is a click rather than a wall of config.