Linkwarden
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 in full text. For every link it saves, Linkwarden automatically preserves a screenshot, a PDF, a single-file HTML copy and a readable text snapshot. 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.
On the first boot of every deployed VM, the image generates a Linkwarden administrator account, 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 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 and re-provisioned with every VM, and you can resize each tier independently.
What you get:
- 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 and the 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 SecureAnyCloud support
Prerequisites
You need an active Azure subscription, an SSH key pair, and a VNet and subnet in the target region.
Standard_B2ms (2 vCPU / 8 GiB RAM) is a good starting point. Page archiving runs a headless Chromium browser, which benefits from memory.
Configure the network security group (NSG) to allow inbound 22/tcp from your management network and 80/tcp from the networks your users will reach Linkwarden on. For public exposure, front port 80 with TLS (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 SecureAnyCloud, and select Create.
On the Basics tab, 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 select Review + create and 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
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:linkwarden-ubuntu-24-04:default:latest
az vm create \
--resource-group <your-rg> \
--name linkwarden \
--image sac1647283583153:linkwarden-ubuntu-24-04:default:latest \
--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 1010Step 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.txtThe output is 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, such as 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.serviceAll three units report active. The NextAuth session endpoint is open and returns an empty JSON object when you are not signed in. This confirms that the web application is serving through nginx:
curl -fsS http://127.0.0.1/api/v1/auth/session{}You can also prove from the shell that the generated credentials authenticate end to end. This command 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 you sign 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, 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, separate from the operating system disk:
findmnt -no SOURCE,SIZE,FSTYPE,TARGET /var/lib/postgresql
findmnt -no SOURCE,SIZE,FSTYPE,TARGET /opt/linkwardenStep 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 open 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 linkwardenEnabling HTTPS
The image serves Linkwarden over plain HTTP on port 80, so it is reachable immediately on its launch address. For production, terminate TLS. The simplest option is a Let's Encrypt certificate with Certbot. First point a DNS name at the VM and open 443/tcp in the NSG. Then run:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.comAfter enabling HTTPS, update NEXTAUTH_URL in /opt/linkwarden/src/.env to your https://your-domain.example.com/api/v1/auth address. Then 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. Each tier lives on its own Azure data disk, so 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.gzThe image ships with the Ubuntu 24.04 security baseline fully applied at capture time, including phased updates. 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 -1To 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.
SecureAnyCloud support can assist with upgrades and migrations.
Service management reference
- 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. SecureAnyCloud provides 24/7 technical support for the image. Support covers 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.
Deploy on Azure
Launch Linkwarden on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com
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 in full text. For every link it saves, Linkwarden automatically preserves a screenshot, a PDF, a single-file HTML copy and a readable text snapshot. 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 instance.
On the first boot of every deployed instance, the image generates a Linkwarden administrator account, a fresh authentication secret and a fresh PostgreSQL password. Two instances launched from the same Amazon Machine Image never share credentials. The administrator username and password and the database password are written to /root/linkwarden-credentials.txt with mode 0600, so 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 EBS volume separate from the operating system disk. The PostgreSQL data directory sits on its own EBS volume at /var/lib/postgresql. You can resize each tier independently of the others.
Prerequisites
Before you deploy this image you need:
- An Amazon Web Services account where you can launch EC2 instances
- IAM permissions to launch instances, create security groups, and subscribe to AWS Marketplace products
- An EC2 key pair in the target Region for SSH access to the instance
- A VPC and subnet in the target Region, with a security group allowing inbound port 22 from your management network and inbound ports 80 and 443 from the networks your users will reach Linkwarden on
- The AWS CLI (version 2) installed locally if you plan to deploy from the command line
Launch the instance from the AWS Marketplace
Sign in to the AWS Management Console, open the EC2 service, and select Launch instance. Under Application and OS Images, choose AWS Marketplace AMIs and search for Linkwarden. Select the SecureAnyCloud listing, choose Select, then choose Continue on the subscription summary.
Pick an instance type of m5.large or larger. Page archiving runs a headless Chromium browser, which benefits from memory and CPU. Choose your EC2 key pair under Key pair (login).
Under Network settings, select your VPC and subnet. Create or select a security group that allows inbound port 22 from your management network and inbound ports 80 and 443 from the networks your users use. Leave the root volume at the default size or larger.
Select Launch instance. First-boot initialisation takes approximately one to two minutes after the instance state becomes Running and the status checks pass, while the administrator account is seeded.
Launch the instance from the AWS CLI
The following command launches an instance from the SecureAnyCloud Linkwarden Marketplace AMI into an existing subnet and security group.
Replace <ami-id> with the AMI ID shown on the Marketplace listing, <key-name> with your EC2 key pair name, and <subnet-id> with your subnet ID. Replace <security-group-id> with a security group that opens ports 22, 80 and 443 as described above.
aws ec2 run-instances \
--image-id <ami-id> \
--instance-type m5.large \
--key-name <key-name> \
--subnet-id <subnet-id> \
--security-group-ids <security-group-id> \
--block-device-mappings '[{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":20,"VolumeType":"gp3"}}]' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=linkwarden-01}]'On success, the command prints a JSON document. Note the instance ID. Once the instance is running, retrieve its public address with aws ec2 describe-instances --instance-ids <instance-id> --query "Reservations[].Instances[].PublicIpAddress" --output text.
Connect and retrieve initial credentials
Connect over SSH with the key pair you selected and the public IP address from Step 2. The SSH login user depends on the operating system of the AMI variant you launched:
- Linkwarden 2.14 on Ubuntu 24.04: SSH login user ubuntu
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.
ssh <login-user>@<public-ip>
sudo cat /root/linkwarden-credentials.txtThe output is 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, such as a password manager or encrypted vault. Do not commit them to source control.
From the same SSH session, confirm that the deployment is healthy. The Linkwarden service runs the web application and the archiving worker together:
systemctl status linkwarden --no-pager● linkwarden.service - Linkwarden (Next.js web + archiving worker) Loaded: loaded (/etc/systemd/system/linkwarden.service; enabled; preset: enabled) Active: active (running) Main PID: 53385 (node) Tasks: 159 CGroup: /system.slice/linkwarden.service ├─ node /usr/bin/corepack yarn concurrently:start ├─ concurrently "yarn workspace @linkwarden/web start" "yarn workspace @linkwarden/worker start" └─ ...The NextAuth session endpoint is open and returns an empty JSON object when you are not signed in. This confirms that the web application is serving:
curl -fsS http://127.0.0.1/api/v1/auth/session{}Sign in to Linkwarden
Open http://<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 you sign in, you land on the Linkwarden dashboard, which shows your collections and recently added links.
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, add any tags, and save. Linkwarden fetches the page title and metadata immediately and queues the page for archiving in the background.
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 volume under /opt/linkwarden/data. They are kept separate from the operating system disk and can be resized or backed up independently.
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 open 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.
Configure outbound email (optional)
Linkwarden can send email for account-related actions when you point it at an SMTP server. Edit the environment file, add your SMTP settings, then restart the service. 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 linkwardenEnable HTTPS (optional)
The image serves Linkwarden over plain HTTP on port 80, so it is reachable immediately on its launch address. For production, terminate TLS. The simplest option is a Let's Encrypt certificate with Certbot. First point a DNS name at the instance.
Install Certbot and request a certificate for your domain. Certbot edits the nginx configuration in place to add the TLS server block and a redirect from HTTP to HTTPS:
sudo snap install --classic certbot
sudo ln -sf /snap/bin/certbot /usr/bin/certbot
sudo certbot --nginx -d your-domain.example.comAfter enabling HTTPS, update NEXTAUTH_URL in /opt/linkwarden/src/.env to your https://your-domain.example.com/api/v1/auth address. Then restart Linkwarden, so the authentication callback URLs match your new address:
sudo systemctl restart linkwardenBackup and maintenance
Two pieces of state matter for backup: the PostgreSQL database (link metadata, users and collections) and the archived files under /opt/linkwarden/data.
Back up the database with pg_dump:
sudo -u postgres pg_dump linkwarden | gzip > linkwarden-$(date +%F).sql.gzBack up the archived files by snapshotting the /opt/linkwarden EBS volume from the AWS console or CLI, or by copying /opt/linkwarden/data to object storage. The database and the application data live on their own EBS volumes, so you can take EBS snapshots of each tier independently.
To update Linkwarden in place, pull the new tag in /opt/linkwarden/src, reinstall dependencies, rebuild and apply migrations, then restart the service. Always take a database dump and an EBS snapshot first.
SecureAnyCloud support can assist with upgrades and migrations.
Service management reference
- Linkwarden service status: systemctl status linkwarden
- Restart Linkwarden: sudo systemctl restart linkwarden
- View Linkwarden logs: sudo journalctl -u linkwarden -f
- 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. SecureAnyCloud provides 24/7 technical support for the image. Support covers 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.
Deploy on AWS
Launch Linkwarden on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com