Applications Azure

Transmute on Ubuntu 24.04 on Azure User Guide

| Product: Transmute on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and configuration of Transmute on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. Transmute is an open source, self-hosted file converter and compressor with a browser interface. You drop in a file, pick a target format and download the result: images, video, audio, documents, spreadsheets, ebooks, subtitles, 3D meshes, fonts and archives are all handled, across thousands of format pairs. Everything runs on your own instance, so the files you convert never leave your Azure subscription and are never uploaded to a third party service.

The cloudimg image ships the free and open source, MIT licensed Transmute application, run the officially supported way as the upstream container pinned by image digest. The container is captured into the VM, so your instance starts in seconds rather than pulling a multi gigabyte image on first boot. Upstream Transmute grants administrator rights to whoever creates the first account, and accepts that request with no authentication while the account list is empty, which is a real risk for a VM with a public IP. This image closes that window completely: on first boot, before the public port is ever opened, the instance generates a per-VM signing key and claims the administrator account itself with a per-VM random password, written to a root-only file. Backed by 24/7 cloudimg support.

Transmute is a trademark of its respective owner. This image is produced by cloudimg and is not affiliated with, endorsed by, or sponsored by the Transmute project. It ships the free and open source self-hosted software, unmodified.

The docker, transmute-firstboot, transmute, transmute-postboot and nginx services all active, the Transmute container healthy and published only to 127.0.0.1:3313, and the readiness endpoint reporting both the database and storage checks as ok

What is included:

  • Transmute v2.0.0 (the MIT licensed self-hosted file converter), pinned by image digest
  • A complete, fully redistributable conversion toolchain inside the image: ffmpeg, Ghostscript, Inkscape, Tesseract OCR, LibreOffice, Pandoc, Calibre and Draw.io
  • Docker Engine running the application container with the app published to loopback only
  • Host nginx as the single public listener on port 80, with a 1 GB upload cap and streaming uploads
  • transmute.service, transmute-firstboot.service and transmute-postboot.service as systemd units, enabled and active on boot
  • A per-VM administrator password and a per-VM token signing key, generated on first boot and never baked into the image
  • The administrator account claimed during bootstrap while port 80 is still closed, so a passer-by can never claim the instance
  • Anonymous and guest conversions disabled by default, so an open converter is never exposed to the internet
  • Automatic clean up of uploaded and converted files on a configurable retention window
  • No default login, no shipped secret, no bundled API key and an empty database on first boot
  • Ubuntu 24.04 LTS base with latest security patches applied at build time
  • Azure Linux Agent for seamless cloud integration and SSH key injection
  • 24/7 cloudimg support with guaranteed 24 hour response SLA

Prerequisites

  • Active Azure subscription, SSH public key, VNet + subnet in target region
  • Subscription to the Transmute listing on Azure Marketplace

Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) is a sensible starting point for a person or a small team converting images, audio and documents. Video transcoding is CPU bound and benefits from more cores, so for regular video work or several concurrent users use Standard_D4s_v5 or larger. Conversions are written to the VM's disk before download, so size the OS disk for the largest files you expect to handle. NSG inbound: allow 22/tcp from your management network and 80/tcp for the web app from the networks that use it.

Step 1: Deploy from the Azure Portal

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Transmute by cloudimg, and select Create. On Basics 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 Review + create and Create.

Step 2: Deploy from the Azure CLI

RG="transmute-prod"; LOCATION="eastus"; VM_NAME="transmute-01"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/transmute/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az network vnet create -g "$RG" --name transmute-vnet --address-prefix 10.100.0.0/16 --subnet-name transmute-subnet --subnet-prefix 10.100.1.0/24
az network nsg create -g "$RG" --name transmute-nsg
az network nsg rule create -g "$RG" --nsg-name transmute-nsg --name allow-ssh --priority 100 \
  --source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp
az network nsg rule create -g "$RG" --nsg-name transmute-nsg --name allow-http --priority 110 \
  --destination-port-ranges 80 --access Allow --protocol Tcp
az vm create -g "$RG" --name "$VM_NAME" --image "$GALLERY_IMAGE_ID" \
  --size Standard_B2s --storage-sku StandardSSD_LRS \
  --admin-username azureuser --ssh-key-values "$SSH_KEY" \
  --vnet-name transmute-vnet --subnet transmute-subnet --nsg transmute-nsg --public-ip-sku Standard

Replace <sub-id> and <version> with your subscription id and the image version shown on the listing, and <your-mgmt-cidr> with the network you administer from.

Step 3: First boot and per-instance credentials

On the first boot of each VM, Transmute generates its own per-instance secrets and claims its own administrator account before the web port is opened. Nothing is baked into the image. Give the instance a minute after first boot, then SSH in and read the credentials file (it is 0600 root:root):

sudo cat /root/transmute-credentials.txt

The per instance credentials file showing the Transmute administrator login generated randomly for this VM (masked here) and the resolved web URL, with both the credentials file and the environment file confirmed as 0600 root:root

The file contains everything unique to this instance:

  • TRANSMUTE_USERNAME / TRANSMUTE_PASSWORD — the administrator login you use in the web UI. The administrator has full access, including user management.
  • TRANSMUTE_URL — the web address of this instance, resolved from its public IP on first boot.

A per-VM token signing key is written alongside them in /opt/transmute/.env, also 0600 root:root. It is generated on this VM and never exists in the image. Persisting it matters: upstream generates a fresh random key in memory whenever the variable is empty, which would silently sign everyone out every time the container restarted.

Confirm the services and container are healthy:

sudo systemctl is-active docker transmute-firstboot transmute transmute-postboot nginx
active
active
active
active
active
sudo docker compose -f /opt/transmute/docker-compose.yml ps --format 'table {{.Service}}\t{{.Status}}\t{{.Ports}}'
SERVICE     STATUS                    PORTS
transmute   Up 52 seconds (healthy)   127.0.0.1:3313->3313/tcp

The application container publishes to loopback only; host nginx on port 80 is the single public door.

Step 4: The security model

A file converter reachable on a public IP is an attractive target: it accepts arbitrary uploads, it spends CPU and disk on them, and media parsers are a classic memory safety attack surface. This image is therefore locked down by default in four ways.

The administrator seat is already taken. Upstream Transmute makes the first account an administrator and accepts that request with no authentication while the account list is empty. transmute-postboot.service claims it first, over the loopback port, with a password generated for this VM. Afterwards the same endpoint refuses forever, which is enforced by the application's own code rather than by a configuration flag.

The public port is closed during bootstrap. Host nginx, the only public listener, does not start until the administrator exists: it is gated on a bootstrap-ready marker that post-boot creates only after the account has been claimed. Port 80 is therefore closed for the whole bootstrap window, so there is no moment in which a stranger could claim the instance.

Nothing is anonymous. Guest and unauthenticated conversions ship disabled, so every upload and every conversion requires a login. There is no open self-registration either: once the administrator exists, creating further accounts requires an administrator token.

The app is not directly exposed. The application listens on 127.0.0.1:3313 only. nginx caps a single upload at 1 GB and streams request bodies rather than spooling them into memory.

ss -tln | grep -E ':(80|3313) ' | awk '{print $1, $4}'
LISTEN 0.0.0.0:80
LISTEN 127.0.0.1:3313
LISTEN [::]:80

Every one of those claims is checked at runtime rather than asserted. You can reproduce the checks yourself:

curl -s -o /dev/null -w 'GET  /api/files              -> %{http_code}\n' http://127.0.0.1/api/files
curl -s -o /dev/null -w 'POST /api/guest/session      -> %{http_code}\n' -X POST http://127.0.0.1/api/guest/session
curl -s http://127.0.0.1/api/users/bootstrap-status; echo
GET  /api/files              -> 401
POST /api/guest/session      -> 403
{"requires_setup":false,"user_count":1}

An anonymous file listing is refused, guest sessions are refused, and the instance reports that setup is complete with exactly one account: the administrator claimed for this VM.

The security posture proven at runtime: the app listening only on 127.0.0.1:3313 with nginx the sole 0.0.0.0:80 listener, an anonymous file listing refused 401, an anonymous attempt to create an account refused 401 because the administrator seat is already taken, guest sessions refused 403, and the bootstrap status reporting setup complete with exactly one user

Step 5: Sign in and convert a file

Open http://<public-ip>/ in a browser. You are sent to the sign in page, because nothing on this instance is public.

The Transmute sign in page, requiring a username and password before any part of the converter is reachable

Sign in with the TRANSMUTE_USERNAME and TRANSMUTE_PASSWORD from the credentials file. You land on the Workspace, which is the converter. Drop a file onto the drop zone or click to browse, then pick the target format on the file's row and choose a quality preset.

The Transmute workspace with a PNG image staged for conversion, the target format set to JPEG at medium quality, showing the source file at 81.2 KB and a Convert 1 File button ready to run

Click Convert. When the job finishes, the file moves to Completed and the download, preview and delete actions appear on its row.

A completed conversion in the Transmute workspace: the source PNG has become sample-photo.jpeg at 56.0 KB in JPEG format, with preview, download and delete actions available on the row

The Jobs page keeps the history of everything this instance has converted, with the resulting format, size and status for each job. Both image and audio conversions are shown here, the latter handled by the ffmpeg pipeline inside the image.

The Transmute Jobs page listing three completed conversions, each with a green success status: a 56.0 KB JPEG produced from a PNG, a 12.3 KB FLAC produced from a WAV through ffmpeg, and a second JPEG output

The preview action renders the file in the browser without downloading it, so you can check a result before saving it.

The Transmute preview modal rendering an image file in the browser, confirming the converted output is a genuine image rather than an empty or corrupt file

Files can also be pulled in by URL rather than uploaded, using the URL for Transmute to grab field on the workspace. That fetch is made by the instance itself, so point it only at sources you trust.

Step 6: Verify a real conversion from the command line

The image ships a prover that drives the whole product through the public front door and checks the output's magic bytes, not merely that a request returned HTTP 200. It uploads a generated PNG and converts it to JPEG, then uploads a generated WAV and converts it to FLAC through ffmpeg, asserting the downloaded bytes really are a JPEG and really are a FLAC.

sudo /usr/local/sbin/transmute-roundtrip.sh
OK round-trip: url=http://<public-ip>/ user=admin; ready=200; anon /api/files 401; admin seat taken (POST /api/users 401); guest 403; wrong password 401; login 200 + JWT; PNG->JPEG output magic ffd8ff (1039 bytes); WAV->FLAC output magic fLaC via ffmpeg

ffd8ff is the JPEG start-of-image marker and fLaC is the FLAC stream marker, so both conversions genuinely produced files in the requested format.

Step 7: The conversion toolchain and its licensing

Transmute delegates to a set of well known open source tools, all of which ship inside the image. Because a converter's dependency stack is where redistribution problems usually hide, the image records the exact inventory it was built with, and the build refuses to proceed if the bundled ffmpeg is a non-redistributable one.

sudo docker exec transmute ffmpeg -hide_banner -version | head -1
ffmpeg version 7.1.5-0+deb13u1 Copyright (c) 2000-2026 the FFmpeg developers
sudo sed -n '7,15p' /usr/share/doc/transmute/MEDIA-STACK.txt
Media + document tooling inside the image (all redistributable):
  ffmpeg 7:7.1.5-0+deb13u1
  ghostscript 10.05.1~dfsg-1+deb13u1
  inkscape 1.4-6
  libreoffice-impress 4:25.2.3-2+deb13u5
  tesseract-ocr 5.5.0-1+b1
  unrar-free 1:0.3.1-1
  ffmpeg banner: ffmpeg version 7.1.5-0+deb13u1 Copyright (c) 2000-2026 the FFmpeg developers
  ffmpeg build : verified NOT --enable-nonfree, and free of libfdk-aac.

The real conversion round-trip run from the command line, asserting a PNG converted to JPEG carries the ffd8ff magic bytes and a WAV converted to FLAC carries the fLaC marker, alongside the ffmpeg version and the recorded redistributable media stack inventory

The full inventory, with the licence of each component, is at /usr/share/doc/transmute/MEDIA-STACK.txt, and the upstream MIT licence is at /usr/share/doc/transmute/LICENSE. ImageMagick is deliberately absent: upstream removes it and its libraries from the image, and the image conversion path uses Pillow and PyMuPDF instead.

Step 8: Retention, users and day to day operation

Uploaded and converted files are cleaned up automatically so a busy instance does not fill its disk. The retention window is set per account from the Settings page and defaults to 60 minutes; raise it if you want results to linger, or lower it if you are handling sensitive material. Deleting a file from the Files or Jobs page removes it immediately.

Additional users are created from the account menu by the administrator. There is no open self-registration, so nobody can create an account on your instance without you.

Everyday service management is ordinary systemd:

sudo systemctl restart transmute
sudo systemctl status transmute --no-pager | head -5

Application logs come from the container:

sudo docker logs --tail 20 transmute

If you reach the VM by a DNS name rather than its IP address, set APP_URL in /opt/transmute/.env to that URL and restart the service so that links the application generates match how people actually reach it.

Troubleshooting

The web page does not load. Check that nginx is running and that the NSG allows 80/tcp from your network. On the very first boot, nginx deliberately stays down until the administrator account has been claimed, which takes a few seconds; sudo systemctl is-active transmute-postboot reporting active means bootstrap finished.

A conversion fails for one particular file. Not every format pair is supported in both directions, and some source files are simply malformed. The Jobs page records the failure against the job; sudo docker logs --tail 50 transmute shows the underlying tool's error.

Large uploads are rejected. nginx caps a single upload at 1 GB. Raise client_max_body_size in /etc/nginx/sites-available/transmute.conf and run sudo systemctl reload nginx if you need more, keeping the VM's disk size in mind.

Video conversions are slow. Transcoding is CPU bound and there is no GPU in the recommended size. Move to a compute optimised VM size for regular video work.

Support

cloudimg provides 24/7 support with a guaranteed 24 hour response SLA for this image: support@cloudimg.co.uk. Questions about Transmute itself belong upstream at github.com/transmute-app/transmute.