LibreDB Studio's Microsoft Marketplace channel: a free ("Get It Now")
solution template that deploys one Ubuntu 24.04 LTS VM running the
ghcr.io/libredb/libredb-studio container behind a Caddy reverse proxy with
automatic HTTPS. The listing texts Partner Center needs are in
listing/listing-fields.md.
| Path | What it is |
|---|---|
src/mainTemplate.json |
ARM template source — __INSTALL_SCRIPT_B64__ is filled at build time |
src/createUiDefinition.json |
Azure portal wizard definition (shipped verbatim) |
src/install.sh |
First-boot installer source — image refs filled at build time |
package-version.txt |
Partner Center package version (single source of truth; bump every publish) |
listing/ |
Listing texts + media requirements (listing-fields.md, description.html) |
node scripts/build-azure-package.mjs # pins package.json version
node scripts/build-azure-package.mjs --version 0.9.66 --package-version 1.0.1Output: dist/azure/libredb-studio-azure-<packageVersion>.zip — exactly two
files at the zip root, images pinned by manifest digest. The build fails if an
apiVersion in the template is ≥700 days old (certification rejects at 730;
warnings start at 540) — refresh values against az provider show when it
warns.
Beside it, dist/azure/build-metadata.json records what the zip pins — app
version, both image digests, and the zip's SHA-256. None of that is recoverable
from the zip itself, so it is what to keep if you ever need to say which images
a submitted package deployed.
In CI: run the Azure Marketplace Package workflow (workflow_dispatch). It
validates the output with Test-AzMarketplacePackage (arm-ttk), uploads the zip
as an artifact, and writes a job summary carrying the digests, the checksum, the
artifact link and the Partner Center steps — so the run itself tells you what to
do next.
The artifact download is a zip containing the package zip, because GitHub wraps artifacts in an archive of its own. Partner Center needs the inner one.
- arm-ttk marketplace suite — zero red (the CI workflow does this).
- Portal sandbox for the wizard: paste
src/createUiDefinition.jsoninto https://portal.azure.com/#view/Microsoft_Azure_CreateUIDef/SandboxBlade. - A real deployment in our own subscription — acceptance criteria below.
- Deployment reports
Succeeded, CustomScript extension included (typical ~5 min). applicationUrlopens and presents a valid HTTPS certificate.- Sign in with the credentials typed into the wizard, then reload the page — the
session must survive. This is not a formality: on plain HTTP the app marks its auth
cookie
Securefor any non-loopback host, the browser discards it, and login loops back silently while every health probe still passes. The installer writesAUTH_COOKIE_SECURE=falseonly for the:80deployment; verify withsudo grep AUTH_COOKIE_SECURE /etc/libredb-studio.env(HTTPS → absent,:80→false). GET https://<fqdn>/api/db/health→{"status":"healthy",…}.- Port 3000 closed from outside and open on the VM; port 22 closed when
sshSourceAddressPrefixis empty;/etc/libredb-studio.envmode0600. - The two secret-bearing directories are private —
stat -c '%a %n' /opt/libredb/data /opt/libredb/caddy/data→700for both. The SQLite store holds connection records with plaintext passwords, and the Caddy data directory holds the TLS private keys; the files inside are created with the containers' umask (0644), so the directory mode is what keeps them away from other local accounts. enableHttps=false+ a/32source range: reachable only from that address, login still survives a reload.authenticationType=password, and a second region, both deploy cleanly.- TLS fallback. Add a
DenyAcmeNSG rule blocking port 80, then deploy with a freshdnsLabelPrefix. Expected: deployment stillSucceeded;curl -fsSk https://<fqdn>/api/db/healthworks while the same probe without-kfails (Caddy's internal CA); noCaddyfile.https/Caddyfile.fallbackexists, because nothing rewrites the config;/etc/libredb-studio.infoexplains the warning and offers no restore procedure. Then delete the NSG rule andsystemctl restart libredb-caddy— a trusted certificate arrives on its own, with no manual step.Let's Encrypt limits failed validations per hostname (5/hour today), so never retry this on a hostname that already burned the budget; use a fresh DNS label.
- VM restart: the app comes back (
systemctl is-enabled libredb-studio) with data intact. - Delete every resource group you created — each holds a running VM, a static public IP and a managed disk.
- Verify the new tag exists on ghcr, then run the CI workflow with
version= app version andpackageVersion= last published + 1 (also bumppackage-version.txtin the same PR). - Deploy the fresh zip into a test subscription; re-run the acceptance criteria above.
- Partner Center → offer → plan → Technical configuration: raise Version, upload the zip → Review and publish → after certification, Go live (human only). Customers keep getting the old package until Go live — there is no outage window.
Cadence: every minor release and every security patch.