This document provides comprehensive information about Youtarr's database setup, configuration, troubleshooting, and management.
- Overview
- Internal Database (Default)
- External Database Setup
- Database Migrations
- Troubleshooting Database Issues
- Storage Considerations
Youtarr uses MariaDB/MySQL for storing:
- Channel subscriptions and metadata
- Video information and download history
- Job queues and processing state
- Session data for authentication
| Table | Model | Description |
|---|---|---|
channels |
Channel |
YouTube channel information |
Videos |
Video |
Downloaded video metadata |
channelvideos |
ChannelVideo |
Channel <-> video associations |
Jobs |
Job |
Download job queue |
JobVideos |
JobVideo |
Job <-> video associations |
JobVideoDownloads |
JobVideoDownload |
Download progress tracking |
Sessions |
Session |
User authentication sessions |
ApiKeys |
ApiKey |
API key credentials for external integrations (bookmarklets, shortcuts, automation) |
SequelizeMeta |
NA | Sequelize ORM migration tracking |
- Image:
mariadb:10.3 - Container Name:
youtarr-db - Port: 3321 inside the Docker network only; the bundled database is not published to the host
- Character Set:
utf8mb4(full Unicode/emoji support) - Default Credentials:
- User:
root - Password:
123qweasd(change in production!) - Database:
youtarr
- User:
volumes:
- ./database:/var/lib/mysql- Data stored in
./databasedirectory on the host - Kept for backwards compatibility with existing bind-mounted installs and plain
docker compose up -dusers - Works well on native Linux Docker hosts
- Can have permission issues on Synology/QNAP
- Can corrupt during MariaDB schema migrations on Docker Desktop for Windows/macOS, ARM hosts, and some virtualized filesystems
volumes:
- youtarr-db-data:/var/lib/mysql- Better compatibility with Synology/QNAP
- Avoids the virtualized-filesystem write semantics problem that can affect bind-mounted MariaDB
- Used automatically for fresh installs started with
./start.shon every platform (Linux included, since v1.69) - Recommended for Docker Desktop on Windows/macOS, ARM systems, and NAS setups
- Not easily visible on host: data lives under
/var/lib/docker/volumes/<project>_youtarr-db-data/_datarather than./database/../scripts/backup.shdumps from the running MariaDB container when it is already up; when it has to start MariaDB for a backup, it detects whether this install uses the bind mount or named volume first.
If you already have Youtarr data in ./database/, do not switch the compose mount by hand unless you intentionally want to start with an empty database. Use the migration helper instead:
./scripts/migrate-to-named-volume.shWhat the script does (in this order, so any failure leaves the simplest possible recovery state):
- Runs a pre-flight permissions check so it fails fast (instead of stalling on an interactive
sudoprompt) if it cannot write to the project directory. - Stops Youtarr.
- Starts the existing bind-mounted MariaDB long enough to run
mysqldumpand to capture per-table row counts. - Renames
./database/to./database.bind-mount-backup.<timestamp>/so the original files are preserved. - Starts a fresh named-volume MariaDB and imports the dump.
- Verifies that the table set matches the source and that every table has the same row count as the source.
- Only after verification succeeds, snapshots
.envto./.env.bak.<timestamp>and pinsCOMPOSE_PATH_SEPARATOR=:andCOMPOSE_FILE=docker-compose.yml:docker-compose.arm.ymlin.env. This means a failure during step 5 or 6 leaves.envuntouched, and recovery is justmv ./database.bind-mount-backup.<timestamp> ./databaseplus removing the partial named volume. - Brings the full stack (app + database) back up so Youtarr is immediately usable.
What the migration does not copy: mysqldump runs with --single-transaction --routines --triggers --events. Schema, data, stored routines, triggers, and events all migrate. MariaDB users and GRANT statements (anything in mysql.user / mysql.db) do not. The default Youtarr install only uses the bundled root user, so this is a no-op for almost everyone. If you have created additional database users on the bundled MariaDB, recreate them after the migration completes.
Password note: for the bundled root database user, DB_ROOT_PASSWORD seeds the root password when a fresh MariaDB data directory is initialized, while Youtarr connects with DB_PASSWORD. The migration requires those two values to match before it creates the new named-volume database.
After it completes, the stack is already running. Subsequent restarts can use any of:
./start.sh # recommended
docker compose up -d # the script pins COMPOSE_FILE in .env
docker compose -f docker-compose.yml -f docker-compose.arm.yml up -d # explicit overrideThe migration is reversible:
- Stop the stack:
./stop.sh
- Restore the
.envsnapshot:mv ./.env.bak.<timestamp> .env
- Remove the named volume for this install. The name is usually
<project>_youtarr-db-data:docker volume ls --format '{{.Name}}' | grep -E '(^|_)youtarr-db-data$' docker volume rm <volume-name>
- Restore the original bind-mounted database directory:
mv ./database.bind-mount-backup.<timestamp> ./database
- Start Youtarr:
./start.sh
Changes made while running on the named volume are not present in the old bind-mounted backup. If you have used the named volume for a while and want to keep those newer changes, take a backup first with ./scripts/backup.sh.
For a new install with no data to preserve, you can start directly with the named-volume override:
docker compose -f docker-compose.yml -f docker-compose.arm.yml up -dOr pin the override in .env so plain docker compose up -d uses it:
COMPOSE_PATH_SEPARATOR=:
COMPOSE_FILE=docker-compose.yml:docker-compose.arm.ymlCOMPOSE_PATH_SEPARATOR=: is important on Windows so Compose parses the file list consistently.
-
Edit
.envfile:DB_USER=youtarr DB_PASSWORD=secure-password-here DB_ROOT_PASSWORD=different-secure-password
-
If using non-root user, uncomment in
docker-compose.yml:environment: - MYSQL_USER=${DB_USER} - MYSQL_PASSWORD=${DB_PASSWORD}
-
Restart containers for changes to take effect
Warning: The bundled database is not exposed to the host by default. If you manually publish port 3321, keep it restricted to trusted hosts only.
- MariaDB 10.3+ or MySQL 8.0+
- Database with
utf8mb4character set - User with full privileges on the database
- Network connectivity from Youtarr container
Run on your database server:
Note: The example below assumes you are using youtarr for your DB name and youtarr for your DB user.
CREATE DATABASE youtarr
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
CREATE USER 'youtarr'@'%'
IDENTIFIED BY 'your-secure-password';
GRANT ALL PRIVILEGES ON youtarr.*
TO 'youtarr'@'%';
FLUSH PRIVILEGES;Replace '%' with specific IP/network if restricting access.
Edit .env file:
DB_HOST=192.168.1.100 # Your database server IP
DB_PORT=3306 # Your database port
DB_USER=youtarr # Database username
DB_PASSWORD=your-secure-password
DB_NAME=youtarr # Database nameUsing convenience script:
./start-with-external-db.shOr manually:
docker compose -f docker-compose.external-db.yml up -dSimply run the normal start script:
./start.sh- Migrations run automatically on container startup
- Tracked in
SequelizeMetatable - Located in
/app/migrations/inside container - Idempotent - safe to run multiple times
# Use the provided script
./scripts/db-create-migration.sh my-migration-name
# Or use npm directly
npm run db:create-migration -- --name my-migration-name-
Always use helpers for idempotent operations:
const { tableExists, columnExists, createTableIfNotExists } = require('./helpers'); if (!await columnExists(queryInterface, 'videos', 'duration')) { await queryInterface.addColumn('videos', 'duration', {...}); }
-
Test migrations in development first
-
Never modify existing migration files
-
Create new migrations for schema changes
InnoDB: Operating system error number 13- MariaDB container fails to start
- Permission denied errors in logs
- Synology/QNAP NAS: MariaDB runs as UID 999, which may not exist
- Docker Desktop/ARM/NAS: virtualized filesystem or permission issues with bind-mounted MariaDB data
- Wrong ownership: Database files owned by incorrect user
- Migrate to named volume (see above)
- Fix permissions:
# Check current ownership ls -la ./database # Fix ownership (adjust UID:GID as needed) sudo chown -R 999:999 ./database
Duplicate column name 'duration'Table 'channelvideos' already exists- Migration errors after crash/restore
Lost or corrupted SequelizeMeta table causing migrations to re-run
With recent updates, migrations are idempotent and self-healing:
-
Simply restart the container:
docker compose down docker compose up -d
-
If errors persist, manually check:
# Connect to database docker exec -it youtarr-db mysql -u root -p123qweasd youtarr # Check SequelizeMeta SELECT * FROM SequelizeMeta; # If missing, migrations will re-run safely
-
Check container status:
docker ps | grep youtarr-db -
Test connection:
# From inside the database container docker compose exec youtarr-db mysql -u root -p123qweasd youtarr
-
Check logs:
docker logs youtarr-db
- Verify credentials match in
.envand database - Check user permissions:
SHOW GRANTS FOR 'youtarr'@'%'; - Ensure user can connect from container IP
- Emoji not saving correctly
- UTF-8 encoding errors
- Question marks in text
Ensure database uses utf8mb4:
-- Check database charset
SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME
FROM information_schema.SCHEMATA
WHERE SCHEMA_NAME = 'youtarr';
-- Convert if needed
ALTER DATABASE youtarr
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;- Per Channel: ~1-2 KB metadata
- Per Video: ~5-10 KB metadata
- Growth Rate: ~10 MB per 1000 videos