ZFile File Listing and Sharing Server on Ubuntu 24.04 on Azure User Guide
Overview
ZFile is an open source file listing and sharing server for individuals and small teams. It mounts many storage backends side by side, from the VM's local disk to S3 compatible object storage, OneDrive, SharePoint, Google Drive, WebDAV, FTP and SFTP, and presents them all as one browsable web site with share links, direct links, short links and rich in browser previews (images with a gallery mode, audio, video, text and code, PDF, Office documents and more). The cloudimg image builds ZFile 4.5.0 entirely from the upstream MIT source, backend and web interface both, so the image contains only the open edition, with the MIT licence text shipped at /opt/zfile/LICENSE. ZFile runs as a systemd service bound to the loopback interface, nginx on port 80 is the only public surface, and a ready to use "Local Storage" source on a dedicated data disk is configured on first boot. Backed by 24/7 cloudimg support.
What is included:
- ZFile 4.5.0 built from the upstream MIT source (Spring Boot backend under OpenJDK 21 + the Vue 3 web interface), running as the
zfilesystemd service - The ZFile web UI and REST API on port
80, fronted by nginx with ZFile bound to loopback only, tuned for large uploads (no request size cap) - No default credential of any kind: the image ships uninitialised, and a unique per VM admin password is generated on first boot to create the admin account
- A pre configured "Local Storage" source rooted at
/var/lib/zfile/fileson a dedicated 32 GiB Azure data disk, which also holds the embedded SQLite database - Share links, direct links and short links per file or folder, with optional passwords and expiry
- An unauthenticated
/healthzendpoint on:80for Azure Load Balancer health probes zfile.service+nginx.serviceas systemd units, enabled and active- 24/7 cloudimg support
Good to know: the ZFile web interface is presented in Chinese (the upstream project's language). The layout is a conventional file manager, every workflow shown in this guide is reproducible from the screenshots alone, and browser page translation works well with it. A fresh VM is also private by default: anonymous visitors see an empty site until you either sign in or explicitly grant anonymous view permissions on a storage source (Step 9).
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet + subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a comfortable starting point for a personal or small team file server; size up for heavy concurrent transfer workloads. NSG inbound: allow 22/tcp from your management network, 80/tcp for the web UI, and 443/tcp if you add TLS. ZFile serves plain HTTP out of the box; for production use, front it with TLS on your own domain.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for ZFile 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). Review the dedicated data disk on the Disks tab, then Review + create -> Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name zfile \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name zfile --port 80 --priority 1010
Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>
Step 4 - Confirm the services are running
systemctl is-active zfile.service nginx.service
Both report active. ZFile itself listens only on the loopback address 127.0.0.1:8080; nginx fronts port 80 as the only public surface. The embedded SQLite database (db/) and the Local Storage file root (files/) live on the dedicated data disk mounted at /var/lib/zfile:
findmnt -no SOURCE,SIZE,FSTYPE /var/lib/zfile
ls -l /var/lib/zfile

Step 5 - Retrieve your admin password
The image ships with no account at all. On the first boot of your VM, a unique admin password is generated, the admin account is created with it, and it is written to a root only file:
sudo cat /root/zfile-credentials.txt
This file contains ZFILE_URL, ZFILE_USERNAME (admin) and ZFILE_ADMIN_PASSWORD. Store the password somewhere safe. The MIT licence and full source provenance for the build ship in the image too:
head -2 /opt/zfile/NOTICE.txt

Step 6 - Confirm the health endpoint
nginx serves an unauthenticated health endpoint for load balancers and probes:
curl -s http://localhost/healthz
It returns ok. This endpoint never requires authentication, so it is safe for an Azure Load Balancer health probe.
Step 7 - Confirm the authentication gate
The following reads the per VM password from the credentials file and proves the round trip - a wrong password is rejected and the correct password authenticates:
PW=$(sudo grep '^ZFILE_ADMIN_PASSWORD=' /root/zfile-credentials.txt | cut -d= -f2-)
curl -s -H 'Content-Type: application/json' \
-d '{"username":"admin","password":"wrong-pw"}' http://127.0.0.1/user/login | jq -r '.code'
curl -s -H 'Content-Type: application/json' \
-d "{\"username\":\"admin\",\"password\":\"$PW\"}" http://127.0.0.1/user/login | jq '{code, admin: .data.admin}'
The wrong password returns error code 42001; the per VM password returns code "0" with admin: true. Anonymous visitors cannot list files: a fresh VM is private until you sign in or grant anonymous permissions (Step 9).

Step 8 - Sign in and browse your files
Browse to http://<vm-public-ip>/ and click through to the sign in page (or open http://<vm-public-ip>/login directly). Enter admin and the password from Step 5.

After signing in, the home page lists your storage sources - the pre configured Local Storage source is already there:

Click Local Storage to enter the file browser. The welcome README.txt seeded by the image is listed with its timestamp and size. Use the toolbar icons to upload files, create folders, and switch to gallery view; right click a file for rename, copy, move, delete, direct link and share link actions.

Double click README.txt to preview it - text and code files open in an in browser editor, and images, audio, video, PDF and Office documents each get their own preview:

Step 9 - Manage storage sources and permissions
Open the settings icon (or browse to http://<vm-public-ip>/admin) to reach the admin area. Under the storage source settings you can edit the pre configured Local Storage source or add more sources - S3 compatible object storage, OneDrive, SharePoint, Google Drive, WebDAV, FTP, SFTP and others - each of which appears alongside Local Storage on the home page.

A fresh VM is private by default: anonymous visitors see an empty storage list. To publish a source as a public file library, open the source's permission settings in the admin area and grant the anonymous role the view (and optionally download) permissions you want. The multi user system under user management lets you create additional accounts scoped to specific sources or directories.
Step 10 - Upload a file and share it by link
Everything in the UI is also scriptable through the same REST API, which is how the image proves itself on every boot. The following uploads a file through the API, generates a share link for it, and downloads it back through the share link:
PW=$(sudo grep '^ZFILE_ADMIN_PASSWORD=' /root/zfile-credentials.txt | cut -d= -f2-)
TOKEN=$(curl -s -H 'Content-Type: application/json' \
-d "{\"username\":\"admin\",\"password\":\"$PW\"}" http://127.0.0.1/user/login | jq -r '.data.token')
echo 'hello from zfile on azure' > hello.txt
curl -s -X PUT -H "zfile-token: $TOKEN" -F 'file=@hello.txt' \
http://127.0.0.1/file/upload/local/hello.txt | jq -r '.code + " " + .msg'
SHARE=$(curl -s -H 'Content-Type: application/json' -H "zfile-token: $TOKEN" \
-d '{"storageKey":"local","sharePath":"/","shareEntries":[{"name":"hello.txt","type":"FILE"}],"shareType":"FILE"}' \
http://127.0.0.1/api/share/create | jq -r '.data.shareKey')
curl -s -L "http://127.0.0.1/api/share/download/$SHARE?path=/hello.txt"
The upload returns 0 ok, and the share link download prints hello from zfile on azure - a byte identical round trip through nginx on port 80. In the web UI the same share action is on the file's right click menu, and the generated http://<vm-public-ip>/share/<key> page can be sent to anyone; share links support optional passwords and expiry dates.

Server Components
| Component | Version | Detail |
|---|---|---|
| ZFile | 4.5.0 | Built from the upstream MIT source (backend + web UI), /opt/zfile/app.jar + /opt/zfile/static/ |
| OpenJDK | 21 (headless JRE) | Runtime for the Spring Boot backend |
| nginx | Ubuntu 24.04 archive | Port 80 reverse proxy, the only public surface |
| SQLite | embedded | ZFile's database at /var/lib/zfile/db/zfile on the data disk |
| Ubuntu | 24.04 LTS | Fully patched at image capture, unattended security upgrades enabled |
Filesystem Layout
| Mount Point | Size | Description |
|---|---|---|
| / | 30 GB | Root filesystem |
| /boot/efi | 1 GB | UEFI boot partition (Gen2 Hyper V) |
| /var/lib/zfile | 32 GB | Dedicated data disk: db/ (SQLite database) + files/ (Local Storage root) |
| /mnt | varies | Azure temporary resource disk |
Key directories:
| Path | Purpose |
|---|---|
| /opt/zfile/app.jar | The ZFile server (built from MIT source) |
| /opt/zfile/static/ | The web interface (built from MIT source) |
| /opt/zfile/LICENSE | The MIT licence text |
| /opt/zfile/NOTICE.txt | Source provenance (repos, tag, commit) |
| /var/lib/zfile/files/ | Local Storage source root - uploaded files land here |
| /var/lib/zfile/db/ | Embedded SQLite database |
| /var/log/zfile/ | Application logs |
| /root/zfile-credentials.txt | Per VM admin credentials (root only, 0600) |
Managing the service
sudo systemctl status zfile.service # status
sudo systemctl restart zfile.service # restart ZFile
sudo systemctl restart nginx.service # restart the reverse proxy
sudo journalctl -u zfile.service -e # recent logs
Troubleshooting
The web UI shows an empty site or "please select a storage source" with nothing listed
You are browsing anonymously. A fresh VM is private by default - sign in as admin (Step 5) to see the pre configured Local Storage source, or grant anonymous view permissions on the source (Step 9) to publish it.
Sign in fails
Re-read the exact password: sudo cat /root/zfile-credentials.txt (the value after ZFILE_ADMIN_PASSWORD=). Repeated failed logins are rate limited (10 per minute); wait a minute and retry.
HTTP 502 from nginx
ZFile is starting or has stopped. Check systemctl status zfile.service and sudo journalctl -u zfile.service -e. The Java backend takes roughly 20 to 40 seconds to start on a B2s.
Uploads fail for large files
nginx on this image imposes no request size cap (client_max_body_size 0), so check free space on the data disk first: df -h /var/lib/zfile. To grow it, resize the Azure data disk and then the filesystem (sudo resize2fs /dev/disk/azure/scsi1/lun0).
I locked myself out / lost the admin password
The password is only stored hashed inside the database. Simplest recovery on a fresh appliance: sudo systemctl stop zfile nginx, sudo rm -rf /var/lib/zfile/db, sudo rm /var/lib/cloudimg/zfile-firstboot.done /var/lib/cloudimg/zfile-bootstrap-ready, then sudo reboot - first boot runs again, regenerates a fresh password into /root/zfile-credentials.txt and recreates the Local Storage source. (This resets ZFile's settings and accounts; files under /var/lib/zfile/files are untouched.)
On Startup
zfile-firstboot.service runs once on the first boot of the VM: it generates the unique admin password, initialises ZFile with it, creates the Local Storage source, proves a real upload round trip through the API, writes /root/zfile-credentials.txt, and only then allows nginx (the public surface) to start - so the site is never reachable in an uninitialised state. On every later boot the service exits immediately (sentinel file /var/lib/cloudimg/zfile-firstboot.done).
Security Recommendations
- Add TLS for production: front the VM with your own domain and terminate TLS on
:443(for example with certbot), then restrict:80in your NSG. - Keep anonymous access deliberate: only grant anonymous permissions on sources you intend to be public; use the per source password and the multi user system for anything else.
- Restrict SSH: limit
22/tcpto your management IP range in the NSG. - Loopback binding: ZFile is bound to
127.0.0.1:8080in/etc/systemd/system/zfile.service- keep it that way; nginx is the only intended public path. - Security patches: unattended-upgrades remains enabled so the OS continues to receive security updates automatically.
Support
cloudimg provides 24/7 expert support for this image. Contact support@cloudimg.co.uk.