Linkwarden on Ubuntu 24.04 on Azure User Guide
Overview
This image runs Linkwarden, the open source self-hosted bookmark manager and web archive. Linkwarden lets you collect links into collections, organise them with tags and search across everything full text. For every link it saves, Linkwarden automatically preserves a screenshot, a PDF, a single file HTML copy and a readable text snapshot, so the content survives even when the original page changes or disappears.
The Next.js web application is served on port 3000 and a background worker performs the page archiving. An nginx reverse proxy on port 80 sits in front of the application. A local PostgreSQL 16 database holds the link metadata. Page archiving uses Playwright Chromium for screenshots and PDFs and the monolith engine for full page HTML capture, all on the same VM.
A Linkwarden administrator account is generated on the first boot of every deployed VM, together with a fresh authentication secret and a fresh PostgreSQL password. Two VMs launched from the same image never share credentials. The administrator username and password and the database password are written to /root/linkwarden-credentials.txt with mode 0600 so that only the root user can read them. Open self registration is disabled, so no one can sign themselves up; the administrator adds further users from the Linkwarden settings.
The Linkwarden application tree and its archived files live under /opt/linkwarden, on a dedicated Azure data disk separate from the operating system disk. The PostgreSQL data directory sits on its own data disk at /var/lib/postgresql. Both disks are captured into the image, re-provisioned with every VM, and each tier can be resized independently.
What is included:
- Linkwarden 2.14 built from source (Node.js 22 LTS, Yarn 4)
- nginx reverse proxy on
:80in front of the Next.js application on loopback:3000 - PostgreSQL 16 on a dedicated 20 GiB data disk at
/var/lib/postgresql - Application tree and archived files on a dedicated 40 GiB data disk at
/opt/linkwarden - Playwright Chromium + monolith archiving engine preinstalled
- Per-VM administrator account, authentication secret and database password generated at first boot
linkwarden.service,nginx.serviceandpostgresql.serviceas systemd units, enabled and active- 24/7 cloudimg support
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet + subnet in the target region. Standard_B2ms (2 vCPU / 8 GiB RAM) is a good starting point — the page archiving workload runs a headless Chromium browser, which benefits from memory. NSG inbound: allow 22/tcp from your management network and 80/tcp from the networks your users will reach Linkwarden on (front port 80 with TLS for public exposure — see Enabling HTTPS).
Step 1 — Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Linkwarden 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 two dedicated data disks on the Disks tab, then Review + create → Create.
First boot initialisation takes approximately one to two minutes after the VM starts, while the administrator account is seeded.
Step 2 — Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name linkwarden \
--image <marketplace-image-urn> \
--size Standard_B2ms \
--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 linkwarden --port 80 --priority 1010
Step 3 — Connect and retrieve initial credentials
Connect over SSH as azureuser with the key you supplied at deploy time:
ssh azureuser@<vm-public-ip>
The first boot service seeds the administrator account before sign in is possible, so the credentials file is in place when you log in for the first time:
sudo cat /root/linkwarden-credentials.txt
You will see a plain text file containing the Linkwarden URL, the administrator username (admin), the administrator password, and the PostgreSQL database name, user, and password. Copy these values somewhere secure (a password manager or encrypted vault). Do not commit them to source control.

Step 4 — Confirm the deployment is healthy
The Linkwarden service runs the web application and the archiving worker together:
systemctl is-active linkwarden.service nginx.service postgresql.service
All three units report active. The NextAuth session endpoint is open and returns an empty JSON object when you are not signed in, which confirms the web application is serving through nginx:
curl -fsS http://127.0.0.1/api/v1/auth/session
{}

You can also prove the generated credentials authenticate end to end from the shell — this signs in through nginx exactly as the browser does:
USER=$(sudo grep '^linkwarden.admin.user=' /root/linkwarden-credentials.txt | cut -d= -f2-)
PASS=$(sudo grep '^linkwarden.admin.pass=' /root/linkwarden-credentials.txt | cut -d= -f2-)
CJ=$(mktemp)
CSRF=$(curl -s -c "$CJ" http://127.0.0.1/api/v1/auth/csrf | sed -n 's/.*"csrfToken":"\([^"]*\)".*/\1/p')
curl -s -o /dev/null -b "$CJ" -c "$CJ" \
--data-urlencode "username=$USER" --data-urlencode "password=$PASS" \
--data-urlencode "csrfToken=$CSRF" --data-urlencode "redirect=false" --data-urlencode "json=true" \
http://127.0.0.1/api/v1/auth/callback/credentials
curl -s -b "$CJ" http://127.0.0.1/api/v1/auth/session | grep -o '"name":"[^"]*"'
rm -f "$CJ"
"name":"Administrator"
Step 5 — Sign in to Linkwarden
Open http://<vm-public-ip>/ in your browser. The Linkwarden sign in page appears. Enter the administrator username admin and the password from /root/linkwarden-credentials.txt, then select Login.

After signing in you land on the Linkwarden dashboard, which shows your collections and recently added links.

Step 6 — Create a collection and save a link
Collections group related links. Select New Collection in the left sidebar, give it a name and an optional colour and description, and save it. Then select New Link, paste a URL, choose the collection and add any tags, and save. Linkwarden fetches the page title and metadata immediately and queues the page for archiving in the background.

Step 7 — How page archiving works
For every link you save, the background worker preserves the page in several formats so the content is durable:
- A full page screenshot and a PDF, captured with a headless Chromium browser through Playwright
- A single file HTML copy, captured with the monolith engine, which inlines images, stylesheets and scripts into one self contained file
- A readable text snapshot, extracted for distraction free reading and full text search
Open a saved link to see its preserved formats. Archiving runs asynchronously, so the formats appear a few moments after you save the link. The archived files are stored on the dedicated application data disk under /opt/linkwarden, kept separate from the operating system disk:
findmnt -no SOURCE,SIZE,FSTYPE,TARGET /var/lib/postgresql
findmnt -no SOURCE,SIZE,FSTYPE,TARGET /opt/linkwarden

Step 8 — Add more users
Open self registration is disabled on this image, so the public sign up page is closed. As the administrator you add users from the Linkwarden settings. Select your profile, open Settings, then the user administration area, and create accounts for your team. Each user gets their own collections and links and can be granted access to shared collections.
Step 9 — Configure outbound email (optional)
Linkwarden can send email for account related actions when you point it at an SMTP server. The environment file lives at /opt/linkwarden/src/.env — open it with your preferred editor, for example sudo nano /opt/linkwarden/src/.env, set NEXT_PUBLIC_EMAIL_PROVIDER, EMAIL_FROM and EMAIL_SERVER to your provider's values, save the file, and restart Linkwarden:
sudo systemctl restart linkwarden
Enabling HTTPS
The image serves Linkwarden over plain HTTP on port 80 so it is reachable immediately on its launch address. For production use you should terminate TLS. The simplest path is a Let's Encrypt certificate with Certbot once you have pointed a DNS name at the VM and opened 443/tcp in the NSG:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.com
After enabling HTTPS, update NEXTAUTH_URL in /opt/linkwarden/src/.env to your https://your-domain.example.com/api/v1/auth address and restart Linkwarden with sudo systemctl restart linkwarden so the authentication callback URLs match your new address.
Backup and maintenance
Two pieces of state matter for backup: the PostgreSQL database (link metadata, users and collections) on the /var/lib/postgresql data disk, and the archived files under /opt/linkwarden. Because each tier lives on its own Azure data disk, you can snapshot each disk independently from the Azure portal or CLI.
Back up the database with pg_dump:
sudo -u postgres pg_dump linkwarden | gzip > linkwarden-$(date +%F).sql.gz
ls -lh linkwarden-*.sql.gz
The image ships with the Ubuntu 24.04 security baseline fully applied at capture time (including phased updates), and unattended security updates remain enabled for the life of the VM. Keep the OS patched between images with:
sudo apt-get -o DPkg::Lock::Timeout=300 update
sudo apt-get -s dist-upgrade | tail -1

To update Linkwarden in place, pull the new tag in /opt/linkwarden/src, reinstall dependencies with yarn, rebuild and apply migrations, then restart the service. Always take a database dump and disk snapshots first. cloudimg support can assist with upgrades and migrations.
Service management reference
| Task | Command |
|---|---|
| Linkwarden service status | systemctl status linkwarden |
| Restart Linkwarden | sudo systemctl restart linkwarden |
| View Linkwarden logs | sudo journalctl -u linkwarden -n 100 |
| PostgreSQL status | systemctl status postgresql |
| nginx status | systemctl status nginx |
| Retrieve admin credentials | sudo cat /root/linkwarden-credentials.txt |
Support
This is a repackaged open source software product. cloudimg provides 24/7 technical support for the image covering Linkwarden deployment, upgrades, archiving configuration, object storage, search tuning, TLS termination and PostgreSQL administration. Linkwarden itself is licensed under the GNU Affero General Public License version 3. 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.