Enclosed on Ubuntu 24.04 on Azure User Guide
Overview
This guide covers the deployment and configuration of Enclosed on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. Enclosed is a minimalist, open source tool for sending private notes and secrets. You write a note or attach a file, and everything is encrypted in your browser with AES-256 before it ever leaves the page, so the server that stores it only ever holds unreadable ciphertext. Enclosed gives you a single link to share; the key that unlocks the note lives in the link fragment (the part after #), which browsers keep local and never send to a server. The recipient opens the link and reads the note in their own browser, with no account and nothing to install. Before you send, you can lock a note behind an extra password, limit it to a single view so it self-destructs once read, and set it to expire after an hour, a day, a week or a month.
The cloudimg image ships the free and open source, Apache-2.0 licensed Enclosed application, run the officially supported way as the upstream Docker image pinned by digest, behind an nginx reverse proxy on port 80. Because upstream defaults its JWT signing secret to a published placeholder and ships with no login, nothing here ships with a known secret: a per instance login and a per instance JWT signing secret are generated for each VM on first boot, before the app is reachable, and authentication is required to create notes by default so a fresh instance cannot be used by a passer by as an open relay. Backed by 24/7 cloudimg support.
Enclosed is a trademark of its respective owner. This image is produced by cloudimg and is not affiliated with, endorsed by, or sponsored by the Enclosed project. It ships the free and open source self-hosted software, unmodified.

What is included:
- Enclosed v1.16.0 (the Apache-2.0 licensed Node application), run as the official upstream Docker image pinned by image digest
- Docker Engine running the single Enclosed container, its port published to
127.0.0.1:8787(loopback only), fronted by nginx on port80 enclosed.serviceandenclosed-firstboot.serviceas systemd units, enabled and active on boot- A per instance login and a per instance JWT signing secret, generated per VM on first boot, never baked into the image
- Authentication required to create notes by default, so the instance is not a world-writable open relay
- Filesystem storage for the opaque encrypted notes, created empty on first boot
- No default login, no shipped secret and an empty store 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 Enclosed listing on Azure Marketplace
Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) is a sensible starting point. Enclosed is a single lightweight Node service and is comfortable there. NSG inbound: allow 22/tcp from your management network, 80/tcp while you set up, and 443/tcp once you have installed a TLS certificate (see Step 6).
HTTPS is required for real use. Enclosed encrypts and decrypts in the browser using the Web Crypto API, and browsers only expose that API on a secure origin. Over plain
http://to an IP address the encryption cannot run, so the page will not create or read notes. Step 6 covers installing a certificate. Until then, test only fromhttp://localhostthrough an SSH tunnel, which browsers treat as secure (Step 4).
Step 1: Deploy from the Azure Portal
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Enclosed 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="enclosed-prod"; LOCATION="eastus"; VM_NAME="enclosed-01"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/enclosed-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az network vnet create -g "$RG" --name enclosed-vnet --address-prefix 10.100.0.0/16 --subnet-name enclosed-subnet --subnet-prefix 10.100.1.0/24
az network nsg create -g "$RG" --name enclosed-nsg
az network nsg rule create -g "$RG" --nsg-name enclosed-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 enclosed-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 enclosed-vnet --subnet enclosed-subnet --nsg enclosed-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, before the app is reachable, Enclosed generates its own per-instance login and JWT signing secret. 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):
ssh azureuser@<vm-ip>
sudo cat /root/enclosed-credentials.txt

The file contains the account you use to create notes, with a per-instance random password:
ENCLOSED_ADMIN_EMAIL(<ADMIN_EMAIL>) /ENCLOSED_ADMIN_PASSWORD(<ADMIN_PASSWORD>) — the login you use in the web UI to create notes.ENCLOSED_URL(<ENCLOSED_URL>) — the resolved address of this instance.
Confirm the services and container are healthy:
sudo systemctl is-active docker nginx enclosed-firstboot enclosed
active
active
active
active
sudo docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
NAMES STATUS PORTS
enclosed Up 7 minutes 127.0.0.1:8787->8787/tcp
Step 4: Open the app over a secure origin
Enclosed needs a secure origin for its in-browser encryption. Until you have installed a TLS certificate (Step 6), reach it over an SSH tunnel from your workstation, because browsers treat http://localhost as a secure origin:
ssh -L 8080:127.0.0.1:80 azureuser@<vm-ip>
Leave that running and open http://localhost:8080/ in your browser. You are greeted by the login screen, because creating notes requires authentication by default. Sign in with ENCLOSED_ADMIN_EMAIL and ENCLOSED_ADMIN_PASSWORD from Step 3.

Type your secret into the note, and on the right choose an expiry, optionally set a one-time note password, and optionally switch on Delete after reading so the note self-destructs the first time it is opened. Then select Create note.

Enclosed confirms the note was created and gives you a single share link (and a QR code). The decryption key is the part of the link after #; it is generated in your browser and is never sent to the server. Copy the link and send it to your recipient over any channel.

When the recipient opens the link, Enclosed reads the key from the link fragment and decrypts the note in their browser. The plaintext is shown to them, and if you chose delete after reading it is destroyed on the server the moment it is read.

Step 5: The security model
Everything about this image is built so the server never sees your data and nothing usable ships in the image.
- Zero-knowledge by design. Encryption and decryption happen only in the browser; the server stores opaque ciphertext and the decryption key lives in the link fragment, which is never transmitted.
- The app is bound to loopback. The Enclosed container publishes its port to
127.0.0.1:8787only; nginx on port 80 is the single public front door. The app is not reachable directly on the VM's address. - Authentication is required to create notes. A passer by cannot use your instance as an open relay; only the per-instance login can create notes. Recipients still need no account to read a public note.
- No known secret ships. The JWT signing secret and the admin password are generated per VM on first boot, before the app starts, replacing upstream's placeholder default. The app refuses to start if the secret is missing or still the default.
# app on loopback only, public web on :80, unauthenticated create rejected, per-VM secret
ss -tlnH | grep -E ':(80|8787) ' | awk '{print $4}' | sort -u
curl -s -o /dev/null -w 'public web GET / : %{http_code}\n' http://127.0.0.1/
curl -s -o /dev/null -w 'unauth create : %{http_code} (401 = rejected)\n' -X POST http://127.0.0.1/api/notes -H 'Content-Type: application/json' -d '{"payload":"x","deleteAfterReading":false,"ttlInSeconds":3600,"encryptionAlgorithm":"aes-256-gcm","serializationFormat":"cbor-array","isPublic":true}'

A built-in prover exercises the whole model end to end on the instance: it confirms an unauthenticated create is rejected, logs in with the per-VM admin, creates a note, reads the same ciphertext back, proves a delete-after-reading note is gone on the second read, and proves a nonexistent note is refused:
sudo /usr/local/sbin/enclosed-roundtrip.sh
OK round-trip: url=http://<vm-ip> admin=admin@enclosed.local created+readback note <id> (payload matched); burn note <id> refused on 2nd read (404); unauth create 401; ghost 404

Step 6: Install a TLS certificate (required for browser use)
Enclosed depends on the browser's Web Crypto API, which browsers disable on insecure origins. Without HTTPS your users cannot create or read notes except over a localhost tunnel. Point a DNS record at the VM, open port 443 in your network security group, then issue a certificate with Let's Encrypt:
sudo apt-get update
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d enclosed.example.com --redirect --agree-tos -m you@example.com
Certbot obtains the certificate, rewrites the nginx site to listen on 443 with HTTP redirected to HTTPS, and installs a renewal timer. Your users can now open https://enclosed.example.com/ directly and the in-browser encryption works. Replace enclosed.example.com and the email with your own.
Troubleshooting
- The create button does nothing / no notes are created in the browser. You are on an insecure origin. Use the SSH tunnel to
http://localhost:8080/(Step 4) or install a certificate (Step 6). - Login fails. Re-read the exact password with
sudo cat /root/enclosed-credentials.txt; it is a per-VM random value. - Check the app logs.
sudo docker logs --tail 100 enclosedandsudo systemctl status enclosed. - Allow anyone to create notes (open instance). Set
PUBLIC_IS_AUTHENTICATION_REQUIRED=falsein/etc/enclosed/enclosed.envand runsudo systemctl restart enclosed. Only do this behind HTTPS and understand it makes note creation public.
Support
Email support@cloudimg.co.uk for help with certificates, network rules, upgrades or branding. Critical issues receive a one hour average response time, with a 24 hour response SLA.
Find this image on the Azure Marketplace and browse the rest of the catalogue at cloudimg.co.uk/products.