An automated subtitle synchronization tool that runs as a Docker container. It continuously monitors your media directories for video files with out-of-sync subtitles and automatically synchronizes them using three sync engines (ffsubsync, autosubsync, and alass). This is a fork from the software subsyncarr.
Docker Hub: tomtomw123/subsyncarr-plus
- Automated Subtitle Synchronization - Syncs subtitles for your entire media library or specific folders.
- Multiple Sync Engines - Uses ffsubsync, autosubsync, and alass for maximum compatibility and success rate
- Scheduled Processing - Runs on a configurable cron schedule (default: daily at midnight) and on container startup
- Parallel Processing - Configure concurrent subtitle processing for faster library syncing
- Skip Already Synced Files - Avoids re-processing files that already have synchronized subtitles or where an engine repeatedly fails.
- Processing History - View past runs with detailed statistics, results, and logs
- Configuration Dashboard - View current settings, monitored paths, and schedule status
- Configurable Timeouts - Set per-engine timeout limits to prevent hung processes
- Log Management - Configurable retention policies with automatic trimming and deletion
- Non Destructive - Creates new files for each engine so no original files are altered. Allows easy switching between engines while watching content.
- Create a docker-compose.yaml file with the following content:
name: subsyncarr-plus
services:
subsyncarr-plus:
image: tomtomw123/subsyncarr-plus:latest
container_name: subsyncarr-plus
user: '1000:10'
ports:
- '3000:3000' # Web UI
volumes:
# Mount your media directories
- /path/to/movies:/movies
- /path/to/tv:/tv
- /path/to/anime:/anime
- ./data:/app/data # Persist database across restarts
restart: unless-stopped
deploy:
resources:
limits:
memory: 768M # Hard limit
reservations:
memory: 128M # Minimum guaranteed memory
environment:
- TZ=Etc/UTC # Replace with your own timezone
- PUID=1000
- PGID=10
- CRON_SCHEDULE=0 0 * * * # Runs every day at midnight by default
- SCAN_PATHS=/movies,/tv # Comma-separated paths to scan
- EXCLUDE_PATHS=/movies/temp,/tv/downloads # Optional: exclude directories
- MAX_CONCURRENT_SYNC_TASKS=1 # Number of parallel processing tasks
- INCLUDE_ENGINES=ffsubsync,autosubsync,alass # Engines to use-
Update the configuration:
- Replace
/path/to/movies,/path/to/tv, etc. with your actual media paths - Update
TZto your timezone (e.g.,America/New_York,Europe/London) - Update
PUIDandPGIDto match your user (runidcommand to find these) - Adjust
SCAN_PATHSto match your mounted volumes
- Replace
-
Start the container:
docker compose up -d- Access the Web UI:
Open your browser to http://localhost:3000 or whatever port you've mapped to inside docker.
docker run -d \
--name subsyncarr-plus \
--user 1000:10 \
-p 3000:3000 \
-v /path/to/movies:/movies \
-v /path/to/tv:/tv \
-v ./data:/app/data \
-e TZ=Etc/UTC \
-e PUID=1000 \
-e PGID=10 \
-e CRON_SCHEDULE="0 0 * * *" \
-e SCAN_PATHS=/movies,/tv \
-e MAX_CONCURRENT_SYNC_TASKS=1 \
tomtomw123/subsyncarr-plus:latest| Variable | Default | Description |
|---|---|---|
SCAN_PATHS |
/scan_dir |
Comma-separated directories to scan for SRT files (must be mounted as volumes) |
EXCLUDE_PATHS |
(none) | Comma-separated directories to exclude from scanning |
CRON_SCHEDULE |
0 0 * * * |
Cron expression for sync schedule (daily at midnight), or disabled to turn off |
MAX_CONCURRENT_SYNC_TASKS |
1 |
Number of subtitle files to process in parallel (higher = faster but more CPU) |
INCLUDE_ENGINES |
ffsubsync,autosubsync,alass |
Which sync engines to use (comma-separated) |
SYNC_ENGINE_TIMEOUT_MS |
1800000 |
Timeout for each sync engine in milliseconds (30 min default) |
NODE_OPTIONS |
--max-old-space-size=512 |
Node.js options, used here to set memory limit (in MB) |
TZ |
(system) | Timezone for logging and cron scheduling (e.g., America/New_York) |
PUID |
1000 |
User ID for file permissions (run id -u to find yours) |
PGID |
1000 |
Group ID for file permissions (run id -g to find yours) |
| Variable | Default | Description |
|---|---|---|
DB_PATH |
/app/data/subsyncarr-plus.db |
SQLite database location |
LOG_BUFFER_SIZE |
1000 |
Ring buffer size for in-memory logs |
RETENTION_KEEP_RUNS_DAYS |
30 |
Keep complete runs for N days |
RETENTION_TRIM_LOGS_DAYS |
7 |
Trim logs after N days (keeps summary only) |
RETENTION_MAX_LOG_SIZE |
10000 |
Max size for trimmed logs in bytes |
RETENTION_CLEANUP_INTERVAL_HOURS |
24 |
How often to run cleanup (in hours) |
The SYNC_ENGINE_TIMEOUT_MS environment variable controls how long each sync engine can run before being terminated. This prevents hung processes from blocking the queue.
Example configuration:
environment:
- SYNC_ENGINE_TIMEOUT_MS=3600000 # 60 minutes for large filesYour media directory should be organized with video files and their corresponding subtitle files using matching names:
/movies
├── Movie Title (2024).mkv
├── Movie Title (2024).srt # Will be synchronized
├── Movie Title (2024).ffsubsync.srt # Generated output
└── Another Movie.mp4
└── Another Movie.srt
/tv
├── Show Name/
│ ├── Season 01/
│ │ ├── Show.S01E01.mkv
│ │ └── Show.S01E01.srtThe app follows standard naming conventions compatible with Plex, Jellyfin, Emby, and Bazarr.
Subsyncarr Plus includes a comprehensive web-based monitoring interface accessible at http://localhost:3000 after starting the container.
Real-time Monitoring:
- Live progress bars showing current processing status
- File-by-file status updates via WebSocket
- Engine-level detail (see which sync engine is running)
- Current and queued files display
Manual Control:
- Start Full Run - Process all configured directories immediately
- Scan Specific Path - Process a custom directory on demand
- Stop Processing - Cancel all remaining files in current run
- Skip File - Cancel processing for individual files
File Management:
- View completed and skipped files
- Clear processed files from the UI
- Track file status (pending, processing, completed, skipped, error)
- See matched video files for each subtitle
Processing History:
- Sortable run history table with timestamps
- Per-run statistics (total, completed, skipped, failed counts)
- Engine-level results summary with notation:
- F = ffsubsync result
- Au = autosubsync result
- Al = alass result
- Duration tracking for each run
- View detailed logs for any past run with copy-to-clipboard functionality
Configuration Dashboard:
- Display of monitored paths and excluded paths
- Schedule status with next run time
- Human-readable cron schedule translation
Processing history is stored in SQLite and persists across container restarts. Ensure the data volume is mounted:
volumes:
- ./data:/app/data # Database and logs stored hereThe app intelligently tracks failures for each file/engine combination. After 3 consecutive failures, that engine will be automatically skipped for that specific file, preventing wasted processing time. You can reset skip status via the API endpoint /api/skip-status/reset.
Optimized for low-memory environments with:
- Configurable memory limits (768MB default, 128MB minimum)
- SQLite optimizations for low RAM usage
- File-based logging with buffering to reduce memory pressure
- Automatic database vacuuming and cleanup
- Ring buffer for in-memory logs
Automatic cleanup keeps your database size manageable:
- Complete runs retained for 30 days (configurable)
- Logs trimmed after 7 days, keeping only summaries
- Runs beyond retention period are automatically deleted
- Cleanup runs every 24 hours (configurable)
docker logs -f subsyncarr-plusDetailed processing logs are available in the Web UI under "Processing History" - click on any run to view full logs.
If you encounter permission errors, ensure PUID and PGID match your host user:
id -u # Get your user ID
id -g # Get your group IDThen update your docker-compose.yaml with these values.
If the container is being killed due to OOM (Out Of Memory):
- Reduce
MAX_CONCURRENT_SYNC_TASKSto 1 - Increase memory limit in
NODE_OPTIONS(e.g.,--max-old-space-size=1024) - Increase memory limit in docker-compose.yaml
- Reduce
SYNC_ENGINE_TIMEOUT_MSfor faster timeouts - Exclude large files or problematic directories with
EXCLUDE_PATHS
Check that:
- Your subtitle files are named to match video files (e.g.,
movie.mkvandmovie.srt) SCAN_PATHSmatches your mounted volumes- Files haven't already been synced (check for
.ffsubsync.srtfiles) - Files aren't being auto-skipped due to repeated failures (check skip status in Web UI)
Pull the latest image:
docker pull tomtomw123/subsyncarr-plus:latestDocker Hub Repository: tomtomw123/subsyncarr-plus
Issues and pull requests are welcome! Please report bugs or suggest features via GitHub Issues.
See LICENSE file for details.