Unmanic
Overview
Unmanic is a self-hosted media library optimiser. Point it at a library of video files and it keeps the library in one consistent format, with no manual conversions. A scheduled scanner and a filesystem event monitor watch the library, and every file is tested against a configurable plugin flow. Files that do not match your target are queued.
A pool of workers then transcodes the queued files with ffmpeg and replaces each source with the optimised version. The web dashboard shows live worker progress, the pending queue and a full history of completed tasks. A REST API drives the same engine from scripts.
The SecureAnyCloud image installs the pinned Unmanic 0.4.0.post32 into a dedicated Python virtual environment under systemd. It also closes the gaps that make stock Unmanic unsuitable for a public cloud VM. Stock Unmanic has no authentication on the web UI or the API, and its default configuration listens on every network interface. Upstream distributes it mainly as a Docker image, where the container network hides this, but the documented pip install inherits it.
This image binds Unmanic to loopback only. An nginx reverse proxy in front of it requires the admin credential for the entire UI, the websocket and the whole API surface, including mutating routes such as settings/write and plugins/install. The password is generated uniquely on the first boot of your VM and is never baked into the image. An unauthenticated /healthz endpoint stays open for load balancer probes.
The image is also configured to do useful work on day one. Unmanic core does no transcoding by itself, because plugins do the work, and a clean install has no worker groups, so a stock deployment queues files and processes none of them. This image pre-installs and enables the official Transcode Video Files plugin, creates a worker group, and targets H.264 in a Matroska container.
Three short synthetic demo clips, created by ffmpeg itself with no third-party media, are seeded into the library on first boot. Your first login shows a completed transcode rather than an empty dashboard.
What you get:
- Unmanic 0.4.0.post32 in a dedicated virtualenv at
/opt/unmanic/venv, running as theunmanicsystemd service - The Unmanic web UI, websocket and REST API on
:80, fronted by nginx, with Unmanic bound to127.0.0.1:8888only - Per-VM HTTP Basic Auth (user
admin) that protects the entire UI and API, with a unique password generated on first boot - The official
video_transcoderplugin (v0.1.20), pre-installed and enabled, targeting H.264/libx264 into Matroska - A worker group with one worker, so queued files are processed
- ffmpeg from the Ubuntu 24.04 archive, with no third-party PPA and no non-free codec bundle
- A dedicated 40 GiB data disk at
/var/lib/unmanicthat holds the library, the transcode cache and all application state - Three synthetic demo clips seeded into the library on first boot, so the queue and history show real work
- An unauthenticated
/healthzendpoint for Azure Load Balancer health probes - Ubuntu 24.04 LTS base with the latest patches and unattended security upgrades enabled
- 24/7 SecureAnyCloud support
Prerequisites
- An active Azure subscription
- An SSH key pair
- A VNet and subnet in the target region
- A VM size of Standard_B2s (2 vCPU / 4 GiB RAM) or larger. Standard_B2s is the recommended starting point and is enough for the bundled demo library
- NSG inbound rules that allow
22/tcpfrom your management network,80/tcpfor the UI and API, and443/tcpif you add TLS
Transcoding is CPU-bound. For a real library, size the VM for the throughput you need and raise the worker count to match (see Maintenance). Unmanic serves plain HTTP on port 80, so terminate TLS in front of it with your own domain and restrict access to trusted IP ranges.
Codec licensing. ffmpeg in this image comes from Ubuntu's own archive, so the image contains only what Canonical already distributes for Ubuntu 24.04. The H.264/H.265 encoders available are the open-source x264/x265 builds. Patent licensing for using or distributing those codecs in your jurisdiction remains your responsibility. This image grants no codec licence.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, select Create a resource, search the Marketplace for Unmanic by SecureAnyCloud, and select Create.
On the Basics tab, pick your subscription, resource group, region and size. Under Administrator account, choose SSH public key and paste your key. Under Inbound port rules, allow SSH (22) and HTTP (80). Then select Review + create -> Create.
Step 2 - Deploy from the Azure CLI
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:unmanic-ubuntu-24-04:default:latest
az vm create \
--resource-group <your-rg> \
--name unmanic \
--image sac1647283583153:unmanic-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard \
--os-disk-delete-option Delete \
--nic-delete-option DeleteThen open the UI port:
az vm open-port --resource-group <your-rg> --name unmanic --port 80 --priority 1010Step 3 - Connect and confirm the services are running
Connect over SSH as azureuser. Confirm that both services are active, that Unmanic is listening on loopback only, and that the data disk is mounted:
systemctl is-active unmanic.service nginx.servicess -tln | grep -E ':8888|:80 'Unmanic must appear only on 127.0.0.1:8888, and nginx on :80. Unmanic is never exposed to the network directly. nginx is the only public path, and it requires a password.
findmnt -no SOURCE,TARGET,FSTYPE /var/lib/unmanicThe data disk is mounted by UUID from /etc/fstab, not by device node. Device names such as /dev/sdc vary with VM size and disk layout, so never key a mount on them:
grep unmanic /etc/fstabStep 4 - Retrieve your per-VM password
The web UI password is generated on the first boot of your VM by unmanic-firstboot.service and written to a root-only file. It is unique to your VM and is not in the image:
sudo cat /root/unmanic-credentials.txtThe nginx Basic Auth file stores only a bcrypt hash, so the plaintext password is not anywhere in the image:
sudo cut -c1-32 /etc/nginx/.unmanic.htpasswdStep 5 - Confirm the authentication gate
Every route is behind Basic Auth except the health probe. The checks below show that the gate is enforced, including on a mutating API route. A positive control shows that the rejections are an authentication decision and not a missing endpoint:
curl -s -o /dev/null -w 'unauth /healthz -> HTTP %{http_code}\n' http://127.0.0.1/healthzcurl -s -o /dev/null -w 'unauth dashboard -> HTTP %{http_code}\n' http://127.0.0.1/unmanic/ui/dashboard/curl -s -o /dev/null -w 'unauth POST settings/write -> HTTP %{http_code}\n' \
-X POST -H 'Content-Type: application/json' -d '{"settings":{"debugging":false}}' \
http://127.0.0.1/unmanic/api/v2/settings/writeThe health probe returns 200, and both the dashboard and the mutating API route return 401. Now repeat the mutating call with the per-VM password. It must return 200:
PW=$(sudo grep '^UNMANIC_PASSWORD=' /root/unmanic-credentials.txt | cut -d= -f2-)
curl -s -o /dev/null -w 'authenticated POST -> HTTP %{http_code}\n' \
-u "admin:$PW" -X POST -H 'Content-Type: application/json' \
-d '{"settings":{"debugging":false}}' \
http://127.0.0.1/unmanic/api/v2/settings/writeStep 6 - Sign in to the dashboard
Browse to http://<your-vm-public-ip>/. Your browser prompts for credentials. Enter admin and the password from Step 4. Unmanic redirects to the dashboard.
The dashboard has three panels. Workers shows the worker pool and what each worker is doing right now, updating live over a websocket. Pending Tasks is the queue of files the scanner has flagged. Completed Tasks is the history of finished transcodes.
Expand Completed Tasks to see the full history. The three seeded demo clips have each been transcoded on your VM:
Step 7 - Review the library configuration
Go to Settings -> Library. The bundled library is named Media Library and points at /var/lib/unmanic/library on the dedicated data disk. Both the periodic Library scanner and the File monitor are enabled, so new files are picked up when they appear or on the next scheduled scan.
Step 8 - Review the transcoding plugin
Go to Settings -> Plugins. The official Transcode Video Files plugin is pre-installed and enabled. The plugin does the transcoding, because Unmanic core only scans and schedules.
The plugin is configured to encode to H.264 (libx264) and write to a Matroska (.mkv) container. Writing to MKV rather than keeping the source container matters. A container such as AVI cannot carry H.264/H.265 cleanly, and keeping it produces files that do not play. Confirm what the pipeline produced:
sudo bash -c 'for f in /var/lib/unmanic/library/*.mkv; do printf "%-34s " "$(basename $f)"; ffprobe -v error -select_streams v:0 -show_entries stream=codec_name -of default=nw=1:nk=1 "$f"; done'Then read the task history over the authenticated API:
PW=$(sudo grep '^UNMANIC_PASSWORD=' /root/unmanic-credentials.txt | cut -d= -f2-)
curl -s -u "admin:$PW" -X POST -H 'Content-Type: application/json' \
-d '{"start":0,"length":5}' http://127.0.0.1/unmanic/api/v2/history/tasks \
| python3 -c "import json,sys;d=json.load(sys.stdin);print('success:',d['successCount'],'failed:',d['failedCount'])"Step 9 - Point Unmanic at your own media
The demo clips show that the pipeline works. To use your own library, remove them and add your media.
Remove the demo clips:
sudo rm -f /var/lib/unmanic/library/sac-demo-*.mkv /var/lib/unmanic/library/sac-demo-*.aviThen copy your media into /var/lib/unmanic/library/. Copy to a temporary name on the same filesystem and rename the file into place, rather than copying directly into the library.
The file monitor reacts the moment a file appears, so a direct copy can be probed while it is still being written. The probe fails, and the task is recorded as failed until the next scan retries it. A rename within the same filesystem is atomic, so the monitor only sees a complete file:
sudo install -d -o unmanic -g unmanic /var/lib/unmanic/libraryThis is the pattern for each file, shown with the seeded demo media as an example:
sudo bash -c 'cp /opt/unmanic/demo-media/sac-demo-smpte.avi /var/lib/unmanic/library/.incoming-example.avi && chown unmanic:unmanic /var/lib/unmanic/library/.incoming-example.avi && mv /var/lib/unmanic/library/.incoming-example.avi /var/lib/unmanic/library/example.avi && echo "staged and renamed into place"'To keep your media on a separate volume or an Azure Files share, mount it and set the library path to that mount point under Settings -> Library. The path must be readable and writable by the unmanic user, because Unmanic replaces each source file with its optimised version.
Trigger a scan now instead of waiting for the schedule:
PW=$(sudo grep '^UNMANIC_PASSWORD=' /root/unmanic-credentials.txt | cut -d= -f2-)
curl -s -o /dev/null -w 'rescan -> HTTP %{http_code}\n' -u "admin:$PW" \
-X POST -H 'Content-Type: application/json' -d '{}' \
http://127.0.0.1/unmanic/api/v2/pending/rescanMaintenance
Scale the worker pool. One worker is configured, which suits the 2 vCPU starting size. Transcoding is CPU-bound, so on a larger VM raise the count under Settings -> Workers. A reasonable rule of thumb is one worker per 2 vCPUs. Workers beyond the available CPU do not increase throughput.
Change the transcode target. Under Settings -> Plugins, open the settings for Transcode Video Files. H.265/libx265 produces noticeably smaller files than the configured H.264 default but is much slower on CPU, so it suits larger VMs or overnight processing. Keep the destination container as Matroska unless you are sure your chosen container can carry the codec you select.
Where the data lives. All state is on the data disk at /var/lib/unmanic. That includes the library, the worker transcode cache at /var/lib/unmanic/cache, and the application state (configuration database, logs and installed plugins) under /var/lib/unmanic/.unmanic.
The cache matters when you size the disk. In-flight transcodes are written there before they replace the source, so it needs room for the largest file you process. You can snapshot or resize this disk independently of the OS disk.
df -h /var/lib/unmanicManage the services with systemctl:
sudo systemctl restart unmanic.servicesudo journalctl -u unmanic.service -n 30 --no-pagerUnmanic's own application log holds the scanner, worker and post-processor detail:
sudo tail -n 20 /var/lib/unmanic/.unmanic/logs/unmanic.logAdd TLS. For anything beyond a trial, put a domain and a certificate in front of nginx, and restrict port 80/443 to trusted ranges in your NSG. The Basic Auth gate protects the application, but without TLS the password crosses the network in base64.
Security posture. Unmanic's API includes routes that change the library path, install plugin code and browse the host filesystem. In this image all of them sit behind the per-VM Basic Auth gate. Unmanic itself is bound to loopback, so the gate cannot be bypassed. If you place your own reverse proxy in front of this VM, keep an authentication layer and do not expose Unmanic directly.
Patching. The image is patched at build time and ships with unattended security upgrades enabled. Apply the latest updates at any time:
sudo apt-get update && sudo apt-get -y upgradeSupport
SecureAnyCloud provides 24/7 support for this image. Contact sales@secureanycloud.com.
Unmanic is open-source software licensed under the GNU General Public License v3.0. Upstream project documentation is at docs.unmanic.app and the source is at github.com/Unmanic/unmanic.
Deploy on Azure
Launch Unmanic on Ubuntu 24.04 LTS on Azure, with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com
This image delivers Unmanic 0.4, the open-source self-hosted media library optimiser, fully installed and secured behind an nginx reverse proxy. Unmanic watches a library folder and tests every file against a configurable plugin flow. It queues the files that do not conform and transcodes them with ffmpeg through a worker pool. A mixed collection of containers and codecs is normalised to one consistent, space-efficient format, and you never run ffmpeg by hand.
The Unmanic application is a Python service that runs under systemd and listens only on the loopback interface. nginx sits in front on port 80 (and 443 once you add TLS). It proxies the entire web UI and API, plus the live-update WebSocket that the dashboard relies on. The transcoding engine is ffmpeg and ffprobe from the Ubuntu archive.
Unmanic's state tree, the media library and the worker transcode cache all live on a dedicated, independently resizable EBS data volume mounted at /var/lib/unmanic. The volume is separate from the operating system disk.
Secure by default - a per-instance password, generated on first boot
A stock Unmanic install has no authentication on its web UI or its API, and the API includes mutating routes that can rewrite settings and install plugin code. By default it also binds to every network interface. This image fixes both problems. The application is bound to loopback only, and the whole UI and API sit behind an nginx HTTP Basic Auth gate.
The password for the admin user is generated uniquely on first boot and stored only on your running instance. It is never baked into the image. Only an unauthenticated static health endpoint at /healthz is exempt from the gate.
Connecting to your instance
Connect over SSH on port 22. Use the default login user for your operating system variant and the EC2 key pair you launched with.
OS variant SSH login user Example
Ubuntu 24.04 ubuntu ssh -i your-key.pem ubuntu@<instance-public-ip>
Replace <instance-public-ip> with your instance's public IPv4 address. If you reach the instance over a VPN or Direct Connect, use its private address.
Retrieve your web UI password and sign in
The per-instance password is written to a root-owned file on the instance. Read the file over SSH:
sudo cat /root/unmanic-credentials.txtThe file lists the site URL, the username (admin) and the generated password, along with the library and cache paths. Then open Unmanic in your browser:
http://<instance-public-ip>/Your browser prompts for a username and password. This is the nginx Basic Auth gate. Enter admin and the password from the credentials file. The Unmanic dashboard opens.
Enable HTTPS before you expose the instance to the public internet (see Enabling HTTPS below). Until then, restrict inbound access to your own IP in the security group, because the password travels over plain HTTP.
What ships pre-configured
This image is a working transcoding appliance, not a bare install:
- The official transcoding plugin is pre-installed and enabled. Unmanic core does no transcoding on its own, because plugins do the work. This image ships the official Transcode Video Files plugin (
video_transcoder), pre-installed from a pinned offline copy and enabled on the default library. Unmanic's in-app plugin browser proxies the vendor catalogue and returns an empty list for a free installation, so pre-installing the plugin is what makes the appliance work immediately. - A worker group is configured. A clean Unmanic has no workers, so the scanner queues files that nothing processes. This image configures one worker group with one worker, so queued files are transcoded. Raise the worker count on a larger instance (see below).
- The transcode target is a widely playable format. The plugin's stock settings can produce an unplayable file when the source container cannot carry the encoded stream. This image targets H.264 (libx264) written into an MKV container, verified to decode. Switch to HEVC/libx265 for better compression on larger instances if you prefer.
- The library, scanner and file monitor are configured. The default Media Library points at
/var/lib/unmanic/libraryon the data volume. Both the periodic scanner and the filesystem file monitor are enabled.
Demo media and pointing the library at your own files
So that the dashboard, the pending queue and the completed history show real work on your first login, the image seeds three short synthetic demo clips into the library. They are generated at build time by ffmpeg's own test sources, so there is no third-party media and no licensing question. On first boot Unmanic scans the clips, queues them and transcodes them to H.264/MKV, so you can see the pipeline working end to end.
When you are ready to use your own media, remove the demo clips and copy your files in. Copy your collection over SSH from your workstation, for example with rsync. Replace the example values with your own paths and address:
rsync -av -e "ssh -i your-key.pem" /path/to/your/media/ ubuntu@<instance-public-ip>:/tmp/media-upload/Then move the files into the library and hand ownership to the service account, so that the file monitor picks up complete files:
sudo rsync -a --chown=unmanic:unmanic /tmp/media-upload/ /var/lib/unmanic/library/ && rm -rf /tmp/media-uploadTo remove the seeded demo clips, run:
sudo rm -f /var/lib/unmanic/library/sac-demo-*Unmanic scans the library on a schedule (every 60 minutes by default) and watches for new files with the file monitor. To scan immediately, select Rescan library now on the dashboard.
Adjusting the transcode settings
- Workers. In Settings -> Workers, raise the number of workers to match your instance size. One worker already uses multiple cores for a single ffmpeg job. Add workers to transcode several files in parallel on a larger instance.
- Target codec and container. In Settings -> Plugins, open the Transcode Video Files plugin settings. You can change the target video codec (for example to HEVC/libx265 for smaller files at the cost of CPU time), the container and the encoder options.
- Schedule. In Settings -> Library, adjust the scan schedule, or rely on the file monitor to pick up new files almost immediately.
The data volume
All Unmanic state lives on the dedicated EBS data volume mounted at /var/lib/unmanic:
df -h /var/lib/unmanicThat volume holds the state tree (/var/lib/unmanic/.unmanic), the media library (/var/lib/unmanic/library) and the worker transcode cache (/var/lib/unmanic/cache). It is mounted by filesystem UUID through /etc/fstab, so it survives reboots and stop/start.
Keeping the cache on this volume is important. In-flight transcodes are written to the cache, and on the OS disk they could fill root. To grow your storage, expand the EBS volume in the AWS console and then grow the filesystem. Confirm the device name first with lsblk:
sudo resize2fs /dev/nvme1n1Services
Two systemd services keep the appliance running, and a one-shot service runs on first boot:
systemctl status unmanic nginxunmanic.serviceis the Unmanic application, bound to127.0.0.1:8888.nginx.serviceis the reverse proxy and Basic Auth gate on port 80.unmanic-firstboot.serviceis a one-shot service. On first boot it generates the per-instance password, seeds the demo library and checks the auth gate. It does not run again.
Use the unauthenticated health endpoint for load balancer checks. On the instance itself it answers on loopback. From outside, use your instance address:
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/healthzEnabling HTTPS
nginx is ready to terminate TLS on port 443. The simplest route is to point a domain name at your instance and use a Let's Encrypt certificate:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.comcertbot obtains the certificate and rewrites the nginx server block to serve HTTPS and redirect HTTP to HTTPS. The Basic Auth gate stays in place. Alternatively, terminate TLS at an Application Load Balancer in front of the instance. SecureAnyCloud support can help with either approach.
Codec licensing
The transcoding engine uses the open-source H.264/H.265 encoders distributed in the Ubuntu archive. Patent licensing for using or distributing those codecs in your jurisdiction remains your responsibility; this image grants no codec licence.
Support
SecureAnyCloud provides 24/7 technical support for this image by email and live chat - deployment, upgrades, plugin and worker configuration, transcoding troubleshooting, library migration and TLS setup. Email sales@secureanycloud.com; critical issues receive a one-hour average response.
Unmanic is a trademark of its respective owner. All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.
Deploy on AWS
Launch Unmanic on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com