Slink on Ubuntu 24.04 on Azure User Guide
Overview
This guide covers the deployment and configuration of Slink on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. Slink is an open source, self hosted image sharing and upload server. It gives you a private drop in replacement for public image hosts: users upload images through a clean browser interface, each image gets a shareable link, and you keep the files and the metadata on infrastructure you control. It pairs a Symfony REST API with a modern SvelteKit browser client, and stores images and data locally with SQLite by default.
The cloudimg image ships the free and open source, AGPL-3.0 licensed Slink server, run the officially supported way as the upstream container pinned by image digest. The image is captured into the VM, so your instance starts in seconds. Nothing here ships with a known secret: Slink generates a unique application secret and JWT signing keypair for each VM on first boot, and this image seeds a per instance admin account with a random password, before the service is reachable. New account sign ups require administrator approval and anonymous uploads are disabled, so the appliance is private by default. Backed by 24/7 cloudimg support.
Slink is a trademark of its respective owner. This image is produced by cloudimg and is not affiliated with, endorsed by, or sponsored by it. It ships the free and open source AGPL-3.0 licensed self hosted software, unmodified.

What is included:
- Slink server v1.12.3 (the AGPL-3.0 licensed Symfony API and SvelteKit web client), pinned by image digest
- A SQLite application database, created empty on first boot and stored on a persistent volume
- Docker Engine with the application published to the loopback interface only, fronted by nginx on port
80 slink.service,slink-firstboot.service,slink-postboot.serviceandnginx.serviceas systemd units, enabled and active on boot- A unique application secret and JWT signing keypair generated by the app per VM on first boot, plus a per instance admin account with a random password seeded automatically
- Secure defaults: new account sign ups require administrator approval, anonymous guest uploads are disabled, and EXIF metadata is stripped on upload
- No default login, no shipped secret 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 Slink listing on Azure Marketplace
Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) is a sensible starting point for a team or an individual. For heavier upload volumes or many concurrent users use Standard_D2s_v5 or larger. NSG inbound: allow 22/tcp from your management network and 80/tcp for the web client 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 Slink 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="slink-prod"; LOCATION="eastus"; VM_NAME="slink-01"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/slink-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 slink-vnet --address-prefix 10.100.0.0/16 --subnet-name slink-subnet --subnet-prefix 10.100.1.0/24
az network nsg create -g "$RG" --name slink-nsg
az network nsg rule create -g "$RG" --nsg-name slink-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 slink-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 slink-vnet --subnet slink-subnet --nsg slink-nsg --public-ip-sku Standard
Step 3: Connect to your VM
ssh azureuser@<vm-ip>
Step 4: Confirm the services are running
Slink runs as one container under slink.service, fronted by nginx. Confirm the services are active and see the container. nginx listens on :80; the application is published to 127.0.0.1 only:
sudo systemctl is-active docker slink-firstboot slink slink-postboot nginx
sudo docker ps
You will see the services report active and the container running (healthy). The application (127.0.0.1:3000) is bound only to the loopback interface; nginx on :80 is the single public front door, and the SvelteKit client proxies its API calls to the PHP backend inside the container.
Step 5: Read the per instance credentials
Slink generates a unique application secret and JWT signing keypair for this VM on first boot, and this image seeds a per instance admin account with a random password, before the app was reachable, written to a root only file. Read them:
sudo cat /root/slink-credentials.txt
The file (mode 0600 root:root) holds ADMIN_EMAIL and ADMIN_USERNAME (either can be used to sign in), ADMIN_PASSWORD (the random per instance password) and WEB_URL. None of these ship in the image; every value is unique to this VM, so no two instances share a credential and there is no default login to change.

Step 6: Verify the security model
The web client is reachable without authentication, but every protected API endpoint requires a bearer token. Confirm that the web client loads, that an unauthenticated request to a protected endpoint is rejected with 401, and that the per instance admin credential authenticates a real call:
ADMIN_EMAIL=$(sudo grep '^ADMIN_EMAIL=' /root/slink-credentials.txt | cut -d= -f2-)
ADMIN_PASSWORD=$(sudo grep '^ADMIN_PASSWORD=' /root/slink-credentials.txt | cut -d= -f2-)
curl -s -o /dev/null -w 'web client (no auth): %{http_code}\n' -L http://localhost/
curl -s -o /dev/null -w 'user api (no token): %{http_code}\n' -H 'Accept: application/json' http://localhost/api/user
TOKEN=$(curl -s -X POST http://localhost/api/auth/login \
-H 'Content-Type: application/json' -H 'Accept: application/json' \
-d "{\"username\":\"$ADMIN_EMAIL\",\"password\":\"$ADMIN_PASSWORD\"}" | python3 -c 'import sys,json;print(json.load(sys.stdin)["access_token"])')
curl -s -o /dev/null -w 'user api (with token): %{http_code}\n' \
-H "Authorization: Bearer $TOKEN" -H 'Accept: application/json' http://localhost/api/user
The web client returns 200, the protected user endpoint is rejected with 401 for no token, and the same call with the per instance admin bearer token returns 200. This is the security model: the API is gated by a JWT you obtain by signing in with the per instance admin credential.

The same round trip is bundled as a single script on the image, which the build and smoke tests run to prove the credential works end to end:
sudo /usr/local/sbin/slink-roundtrip.sh

Step 7: Open the web client and sign in
Browse to http://<vm-ip>/ to reach the Slink web client. Select Sign In and enter the ADMIN_EMAIL (or ADMIN_USERNAME) and ADMIN_PASSWORD from Step 5. New users can request an account with Sign Up, but each request must be approved by an administrator before it can be used (see Step 10).

Step 8: Upload an image
Select Upload to open the drop zone. Drag an image onto it or click to browse from your device. Slink accepts JPEG, PNG, GIF, WebP, AVIF, SVG and more, up to the configured maximum size. Uploads default to Private, and you can attach tags or add them to a collection before uploading.

Step 9: View your image and copy its share link
After the upload completes, Slink opens the image details page: your image is displayed with its dimensions, format and size, and a ready to use share link you can copy. You can resize the shared version, convert the format (Original, JPG, WebP or AVIF), download the file, add a description, and set an expiry or a password on the share.

Step 10: Administer users and settings
As the admin you have an Administration area with a dashboard, user management and settings. Open Settings to configure image uploads and sharing, storage, and the security policies (user registration, access control and authentication), or Users to approve pending sign ups and manage accounts.

Step 11: Manage registration and approval
By default, users can request accounts with Sign Up but every request must be approved by an administrator before it becomes active, and anonymous guest uploads are disabled. Approve or reject pending users under Administration → Users. You can change the registration, approval and guest upload policies under Administration → Settings → Security Settings. These knobs are also set as environment defaults in /etc/slink/compose.yaml; if you change them there, apply the change with:
sudo systemctl restart slink
Step 12: Call the API from your own machine
Every feature of the web client is backed by the REST API, which you can call directly. Obtain a token by signing in, then call a protected endpoint with the VM address:
TOKEN=$(curl -s -X POST http://<vm-ip>/api/auth/login \
-H 'Content-Type: application/json' -H 'Accept: application/json' \
-d '{"username":"<ADMIN_EMAIL>","password":"<ADMIN_PASSWORD>"}' | python3 -c 'import sys,json;print(json.load(sys.stdin)["access_token"])')
curl -s -H "Authorization: Bearer $TOKEN" -H 'Accept: application/json' "http://<vm-ip>/api/user"
The login returns a JWT bearer token whose payload identifies the admin (ROLE_ADMIN); pass it as an Authorization: Bearer header to reach protected endpoints.
Step 13: Server components
| Component | Version / Detail |
|---|---|
| Application | Slink v1.12.3 (Symfony API + SvelteKit client, pinned by image digest) |
| Front door | nginx on :80 proxying to the app on 127.0.0.1:3000 |
| Application database | SQLite (created empty on first boot, on a persistent volume) |
| Image storage | local filesystem on a persistent volume (EXIF stripped on upload) |
| Orchestration | Docker Compose under slink.service |
| Application secret + keys | per instance, generated by the app on first boot |
| Admin account | per instance random password, seeded first boot into /root/slink-credentials.txt |
| Operating system | Ubuntu 24.04 LTS (patched at build) |
| License | AGPL-3.0 (Slink) |
Step 14: Managing the service
sudo systemctl status slink --no-pager | head -12
sudo docker compose -f /etc/slink/compose.yaml logs --tail 40 slink
Restart the stack with sudo systemctl restart slink, follow the logs live with sudo docker compose -f /etc/slink/compose.yaml logs -f slink, and view configuration in /etc/slink/slink.env and /etc/slink/compose.yaml. After changing the environment file, restart the service to apply it.
Step 15: Use your own domain and HTTPS (production)
The image serves the web client over plain HTTP on port 80, which is convenient behind a load balancer or private network. For production you should terminate TLS in front of the VM so credentials travel encrypted:
- Front the VM with Azure Application Gateway, a load balancer with a managed certificate, or a CDN, and forward to nginx on port
80. - Or install your own certificate: add a TLS
serverblock to/etc/nginx/sites-available/slinkreferencing your certificate and key, open443/tcpon the NSG, thensudo systemctl reload nginx. - Point a DNS name you control at the VM public IP. Because the web client reads its public origin (
ORIGIN) at first boot from the resolved public address, use a stable public IP, or setORIGINin/etc/slink/slink.envto your domain and restart the service when using a custom domain.
Step 16: Security recommendations
- Restrict the NSG. Allow TCP 80 (and 22 for admin) only from the networks that use the app.
- Terminate TLS in front of the app (Step 15) so the admin password and tokens are encrypted in transit.
- Rotate the seeded admin password. Sign in and change it, and keep registration behind admin approval (the default).
- Keep the app private. The container is bound to loopback and fronted by nginx; keep it that way and never expose the container port directly.
- Keep the OS patched. Unattended security upgrades remain enabled on the running VM.
Step 17: Support and Licensing
Slink is distributed under the GNU Affero General Public License v3.0 (AGPL-3.0). This cloudimg image bundles the unmodified official open source release; cloudimg provides the packaging, the nginx front door, the per instance admin generation, the secure by default configuration, the paired deploy guide, and 24/7 support with a guaranteed 24 hour response SLA. Slink is an independent open source project and this image is not affiliated with or endorsed by it.
Deploy on Azure
Launch Slink on Ubuntu 24.04 LTS by cloudimg from the Azure Marketplace and follow this guide to a working self hosted image sharing server in minutes.