LibreSign on Ubuntu 24.04 on Azure User Guide
Overview
LibreSign is an open-source digital document-signing platform from LibreCode coop. It gives an organisation a controlled signing workflow: upload a document, choose who signs it and in what order, track the request, and produce a signed PDF carrying a real cryptographic signature that anyone can validate. LibreSign is built and distributed as an application for Nextcloud, so the appliance in this image is the whole stack rather than one package: the Nextcloud 34.0.3 server that stores the documents and holds the accounts, the LibreSign 14.1.0 application itself, and the complete signing toolchain it needs.
That toolchain is the part that usually goes wrong. On a hand-built instance, LibreSign has to reach out to the internet on first use and download a Java runtime, JSignPdf and pdftk before it can sign anything, and an administrator has to click through that flow. This image does none of that. Every binary is fetched, checksum-verified and installed at build time, so the appliance signs documents the moment it boots, on a network with no outbound access at all.
What is included:
- LibreSign 14.1.0 (AGPL-3.0-or-later) running on Nextcloud 34.0.3 (AGPL-3.0-or-later)
- The complete signing toolchain, pre-provisioned offline: Eclipse Temurin JRE 21.0.8+9, JSignPdf 2.3.0, pdftk-java 3.3.3, poppler-utils and the PHP Imagick extension
- nginx terminating TLS on
:443with a per-VM certificate, and:80redirecting to it - PHP 8.3-FPM over a unix socket, a local PostgreSQL 16 database and a password-protected Redis, all reachable only on loopback
- A dedicated 60 GiB Azure data disk at
/var/lib/libresignholding both your documents and the database - A LibreSign root certificate authority generated uniquely on the first boot of every VM, never baked into the image
- A per-VM administrator password, database password and Redis password generated on first boot and recorded in a root-only file
nginx.service,php8.3-fpm.service,postgresql.serviceandredis-server.serviceas enabled systemd units, plus a five-minute background-jobs timer- 24/7 cloudimg support
cloudimg is not affiliated with, endorsed by or sponsored by LibreCode coop or Nextcloud GmbH. LibreSign and Nextcloud are the trademarks of their respective owners and are used here only to identify the software this image packages.
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B4ms (4 vCPU / 16 GiB RAM) is a good starting point; signing runs a JVM, so do not go below 4 GiB of RAM. NSG inbound: allow 22/tcp from your management network and 443/tcp from wherever your signers are. Port 80/tcp is only useful if you want the automatic redirect to HTTPS to work for people who type a bare address.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for LibreSign 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 HTTPS (443). Review the dedicated 60 GiB 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 libresign \
--image <marketplace-image-urn> \
--size Standard_B4ms \
--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 libresign --port 443 --priority 1010
Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>
Step 4 - Confirm the services are running
The web tier, PHP runtime, database and cache all run as systemd units:
systemctl is-active nginx.service php8.3-fpm.service postgresql.service redis-server.service
Expected output:
active
active
active
active
The background-jobs timer runs Nextcloud's cron.php every five minutes:
systemctl is-enabled libresign-cron.timer
enabled

Step 5 - Retrieve your per-VM credentials
Nothing in this image ships with a known password. On the first boot of every VM, libresign-firstboot.service generates a fresh administrator password, database password, Redis password, TLS certificate and LibreSign root certificate authority, then writes the credentials to a root-only file:
sudo cat /root/libresign-credentials.txt
The file lists the URL to open, the admin account and its password, the database and Redis passwords, this VM's Nextcloud instance id, and the SHA-256 fingerprint of the root certificate authority this VM minted for itself. No two VMs launched from this image share any of them.

Confirm the file is readable only by root:
sudo stat -c '%a %U:%G' /root/libresign-credentials.txt
600 root:root
Step 6 - Sign in to LibreSign
Open https://<vm-public-ip>/ in a browser. The certificate is self-signed for this VM's address, so your browser will warn once; accept it, or replace the certificate with your own as described in Step 11. Sign in as admin with the password from Step 5.

The image sets LibreSign as the default application, so signing in takes you straight to the Request Signatures screen rather than a generic dashboard.
Step 7 - Request a signature
From Request, choose Upload and pick a PDF. Once it is uploaded, select Add signer, search for the account or e-mail address that should sign it, and Save. Repeat for each signer. When the signer list is complete, choose Request signatures and confirm with Send.
The document moves from Draft to Ready to sign, and each signer is listed with their own status.

Step 8 - Sign the document
Choose Sign document. The first time an account signs, LibreSign asks it to create a signing password; that password protects a signing certificate that LibreSign issues to the account from this VM's own root certificate authority. Enter the password and confirm.
LibreSign then applies the signature with the bundled JSignPdf and stamps the signature footer with pdftk. When it finishes, the document reports Signed and LibreSign confirms the signature is valid.

Anyone can check a signed file later from the Validate entry in the left-hand menu, by uploading the PDF or entering the document's identifier.
Step 9 - Verify the signing toolchain
LibreSign will not sign anything unless its external binaries are present, at exactly the pinned versions, and match the SHA-512 manifests that LibreCode signs and ships inside the application. This image satisfies all of that at build time. Check it from the shell at any point:
sudo /usr/local/sbin/libresign-check.sh
Every row reports success: the Temurin JRE, JSignPdf, pdftk, poppler and Imagick, plus the root certificate authority.

The same table is shown in the web interface under Settings -> Administration -> LibreSign, together with the details of the root certificate this VM generated for itself.

Note the wrapper script above rather than calling occ libresign:configure:check directly: that command prints its status table but always exits 0, so its exit code cannot be used as a health check. The wrapper parses the table and fails if any row reports an error.
Step 10 - Check the exposed surface
Only three TCP ports are reachable from outside the VM:
ss -H -ltn | awk '{print $4}' | grep -vE '^(127\.|\[::1\])' | sed -E 's/.*:([0-9]+)$/\1/' | sort -un | tr '\n' ' '
22 80 443
PostgreSQL and Redis are bound to loopback only, and Redis additionally refuses unauthenticated connections:
redis-cli ping
NOAUTH Authentication required.
Plain HTTP only exists to redirect:
curl -s -o /dev/null -w 'HTTP %{http_code} -> %{redirect_url}\n' http://127.0.0.1/
HTTP 301 -> https://127.0.0.1/
An unauthenticated health probe is served on both ports, which is convenient for a load balancer:
curl -ks https://127.0.0.1/health
ok

Step 11 - Use your own domain and certificate
The image trusts the addresses it detected on first boot. To reach the instance on your own DNS name, add it to the trusted domain list and set the canonical URL:
sudo -u www-data php /var/www/nextcloud/occ config:system:set trusted_domains 4 --value sign.example.com
sudo -u www-data php /var/www/nextcloud/occ config:system:set overwrite.cli.url --value https://sign.example.com/
Never set a * wildcard there: it disables the host-header validation that protects password-reset links. Then replace the self-signed certificate with your own, keeping the same paths, and reload nginx:
sudo install -m 0644 fullchain.pem /etc/nginx/ssl/libresign.crt
sudo install -m 0600 privkey.pem /etc/nginx/ssl/libresign.key
sudo systemctl reload nginx
Step 12 - Where your data lives
Both the documents and the database sit on the dedicated data disk, which is captured into the image and re-provisioned on every VM:
df -h /var/lib/libresign | tail -1
/dev/sda 59G 328M 56G 1% /var/lib/libresign
/var/lib/libresign/data is the Nextcloud data directory, which holds your documents and, under appdata_<instance-id>, the pre-provisioned signing toolchain and this VM's root certificate authority. /var/lib/libresign/pgsql/main is the PostgreSQL data directory. Back up that one mount point and you have backed up the appliance's state.
Confirm the platform is the pinned version:
sudo -u www-data php /var/www/nextcloud/occ status
- installed: true
- version: 34.0.3.2
- versionstring: 34.0.3
- edition:
- maintenance: false
- needsDbUpgrade: false
- productname: Nextcloud
- extendedSupport: false
Step 13 - Add the people who will sign
Signers are ordinary accounts. Create them from Settings -> Administration -> Accounts in the browser, or from the shell:
sudo -u www-data OC_PASS='<a-strong-password>' php /var/www/nextcloud/occ user:add --password-from-env --display-name "Jane Doe" jane
LibreSign can also send a request to an e-mail address that has no account yet; configure an SMTP server under Settings -> Administration -> Basic settings first, or those invitations will not be delivered.
Troubleshooting
The browser says the connection is not private. Expected on a fresh VM: the certificate is self-signed for the VM's IP address. Accept it once, or install your own certificate as in Step 11.
"Access through untrusted domain". You reached the VM on a name or address that is not in trusted_domains. Add it as in Step 11.
A signature fails with a Java or JSignPdf error. Run sudo /usr/local/sbin/libresign-check.sh first; it names the failing component. The most common cause on a modified instance is an upgraded binary, because LibreSign requires exactly the pinned versions and rejects newer ones.
First boot did not complete. Check the one-shot unit that mints every secret:
systemctl status libresign-firstboot.service --no-pager | head -20
Where are the logs. nginx logs to /var/log/nginx/, and Nextcloud logs to /var/lib/libresign/data/nextcloud.log.
Support
Every cloudimg image is backed by 24/7 support. Contact support@cloudimg.co.uk with your Azure subscription and VM name.