Self-managed deployment · OpenTofu

Four Ways to Stand Up a Virtuoso

Notes on virtuoso-opentofuOpenLink Software’s self-managed OpenTofu deployment scripts for Virtuoso Universal Server

Executive Summary Notes on a repository published 24 August 2026

Synopsis

virtuoso-opentofu is a set of OpenTofu configurations that provision a single, self-managed Virtuoso Universal Server instance on Amazon Web Services (AWS), Microsoft Azure, or Google Cloud Platform. It exists so an operator can run their own Virtuoso without going near the code that runs OpenLink’s managed service.

The repository holds four provider directories behind one workflow. Each is a self-contained OpenTofu root module with its own versions.tf, variables.tf, outputs.tf, terraform.tfvars.example, deployment helper script, and modules, so the whole interface is cd aws followed by tofu init, tofu validate, tofu plan, tofu apply. Both Virtuoso Open Source 7 and Virtuoso Commercial 8 are supported where the provider-specific deployment documents them, and multiple deployments can share one cloud account provided each gets its own OpenTofu state and a distinct naming prefix.

Each baseline pairs one compute substrate with one storage substrate. AWS runs Virtuoso as a container task on ECS Fargate with the database volume on Elastic File System. Azure’s recommended route is a virtual machine with an attached managed disk. Google Cloud uses a Compute Engine instance running Docker, with a persistent disk and Secret Manager for the generated password. A fourth, Azure Container Instances, is marked experimental: local SQL benchmarking on 18 August 2026 found unacceptable active-database latency even with Premium Azure Files, which is why the plain virtual machine is the Azure recommendation. That finding is about the storage substrate rather than about containers, which is why the Fargate baseline is not demoted alongside it.

The scope is deliberately narrow. OpenLink also sells a managed Virtuoso service, and its control plane, customer and seller portals, Marketplace registration and metering code, and listing evidence are all excluded here by an explicit instruction to contributors. The stated reason is readability: someone who only wants to deploy and manage their own instance should not have to sort deployment code from software-as-a-service machinery they will never run.

Two things have to happen before any baseline reaches production. All four deployments generate a database administrator (DBA) password, and OpenTofu writes generated values into state, so an encrypted remote state backend has to be configured before the first apply rather than after. And container images must be pinned to a tested release tag or an immutable digest: on a database, tracking latest means the engine version can change underneath a live dataset without anyone deciding it should.

“This repository is intended to be understandable to operators who only want to deploy and manage self-hosted Virtuoso infrastructure.”

Query the graph
The four baselines

One Product, Four Provisioning Paths

Each baseline is a separate deployment baseline — a self-contained OpenTofu root module that pairs one compute substrate with one storage substrate. Nothing is shared between them, so reading one directory is enough to deploy it, and a change to one provider cannot regress another.

Baseline 1 of 4: AWS — ECS Fargate + EFS

Virtuoso runs as a container task on Amazon Elastic Container Service (ECS) with the Fargate launch type; the database volume lives on Elastic File System. Because Fargate provisions no virtual machine for the operator to manage, this is the smallest ongoing host-maintenance surface of the four — at the cost of reaching the volume only through the file system AWS attaches.

Baseline 2 of 4: Azure — Virtual Machine + Managed Disk

The recommended Azure route: an Azure virtual machine with an attached managed disk. You inherit host patching and hygiene; in exchange you get a shell, real block storage, and the direct access that makes backup, restore, and diagnosis straightforward.

Baseline 3 of 4: Azure ACI — Container Instances + Azure Files

An experimental variant on Azure Container Instances. It works for light tests. It is not recommended for an active database, and the repository says why with a date and a measurement rather than a hedge.

Baseline 4 of 4: Google Cloud — Compute Engine + Persistent Disk

A Compute Engine instance running Virtuoso under Docker, with a persistent disk for the database volume and Secret Manager holding the generated DBA password. The only baseline of the four to name a dedicated secret store alongside OpenTofu state.

Head to head

The Four Baselines Compared

DimensionAWSAzure VMAzure ACIGoogle Cloud
Compute modelServerless container task — no host to patchVirtual machine — operator owns the hostServerless container instanceVirtual machine running Docker
Storage modelElastic NFS file system (EFS)Attached block storage (Managed Disk)SMB/NFS file share (Azure Files)Durable block storage (Persistent Disk)
Stated maturityAvailableAvailable — recommended for AzureExperimental — light tests onlyAvailable
Secret handlingDBA password in OpenTofu stateDBA password in OpenTofu stateDBA password in OpenTofu stateOpenTofu state plus Secret Manager
Host maintenance surfaceNone — AWS runs the containerFull — OS patching is yours, shell access is tooNone — Azure runs the containerFull — OS patching is yours, shell access is too
Serverless container task — no host to patch
Elastic NFS file system (EFS)
DBA password in OpenTofu state
None — AWS runs the container
Virtual machine — operator owns the host
Attached block storage (Managed Disk)
Available — recommended for Azure
DBA password in OpenTofu state
Full — OS patching is yours, shell access is too
Serverless container instance
SMB/NFS file share (Azure Files)
Experimental — light tests only
DBA password in OpenTofu state
None — Azure runs the container
Virtual machine running Docker
Durable block storage (Persistent Disk)
OpenTofu state plus Secret Manager
Full — OS patching is yours, shell access is too

Read the storage row and the maturity row together and the repository’s reasoning falls out: the two baselines on block storage are both recommended, the one on a file share is experimental, and the one on an elastic NFS file system sits between them — available, but with the volume reachable only through the file system AWS attaches.

Before production

Four Constraints the README Asserts

Configure an encrypted remote state backend

All four deployments generate a DBA password, and OpenTofu records generated values in state. A local terraform.tfstate is therefore a plaintext credential file sitting in your working directory. The repository requires an encrypted remote backend before production use and documents it in docs/REMOTE_STATE.md.

Pin production images to a tested tag or digest

Tracking latest makes the database engine version a moving target: a container restart months after deployment could pull a different build against the same on-disk dataset. A tested release tag makes the version explicit; an immutable digest makes it unforgeable.

One state and one naming prefix per deployment

Multiple deployments are supported inside the same account, subscription, or project. Separate OpenTofu state and a distinct naming prefix such as project_name are what keep a second deployment from being read as drift on the first.

Each provider directory stays self-contained

Every directory carries its own README, versions.tf, variables.tf, outputs.tf, example variables file, helper script, and modules. Nothing crosses provider lines, which is why the deployment interface is identical everywhere without any shared code to keep in step.

Publishing boundary

What This Repository Excludes

Five of the six exclusions mark what this repository does not cover. The sixth is different: state files and terraform.tfvars hold real secrets, so they are kept out of version control for the same reason any password would be.

AWS Marketplace SaaS registration and metering

The billing path of the managed service. It has no role in a deployment you own.

Customer and seller portal source

The web applications through which managed-service customers and OpenLink’s sellers administer subscriptions.

Managed SaaS control plane

The Lambda, API, CodeBuild, and OpenTofu code that provisions instances on customers’ behalf — including its own OpenTofu, which is why the exclusion is stated rather than inferred from file type.

Marketplace private offer JSON

Commercial offer definitions negotiated with individual Marketplace customers.

Marketplace validation evidence

The operational evidence assembled to satisfy Marketplace listing review.

OpenTofu state and local terraform.tfvars

Excluded for a different reason than the other five: it contains real secrets, not managed-service code. State carries the generated DBA password; tfvars carries per-deployment configuration.

Open questions

What the README Leaves Standing

1

Are Kubernetes variants worth the operational cost?

An azure/aks/ variant using AKS with Azure Disk and a gcp/gke/ variant using GKE with Persistent Disk are both named as future options under consideration. Neither is committed — and the tension is visible in the README’s own framing, since the recommended baselines are deliberately single-instance and Kubernetes buys orchestration an operator running one database may not need.

2

Does the ACI variant have a path off Azure Files?

The Container Instances variant is held back by storage latency, not by its compute model. The README records the benchmark but does not say whether a different storage pairing would rehabilitate the variant or whether it stays a light-test option permanently.

3

Is three providers the intended ceiling?

AWS, Azure, and Google Cloud are covered. The future-variant list is stated in terms of Kubernetes flavours of existing providers rather than new providers, leaving whether the provider set itself is closed unstated.

4

When do the provider directories reach the public tree?

As of 24 August 2026 the public repository holds README.md alone. The layout it describes lives on the project’s own remote at devhub.openlinksw.com, which is not publicly resolvable — so the documented interface currently runs ahead of the published code.

How-To

Deploying a Self-Managed Instance

Shown for AWS; the shape is identical in every provider directory. Steps 2 and 4 are the two hardening obligations, placed where they actually have to happen rather than listed afterwards.

1

Choose the provider directory

Pick the baseline that matches your cloud and your tolerance for host maintenance, then change into that directory — for example cd aws. Every subsequent command runs from inside it, because each provider directory is a self-contained OpenTofu root module.

2

Configure an encrypted remote state backend

Do this before the first apply, not after. The deployment generates a DBA password that is written into OpenTofu state, so the state file is a credential store from the moment it exists. The repository points at docs/REMOTE_STATE.md for the backend configuration.

3

Create your variables file from the example

Copy the shipped template with cp terraform.tfvars.example terraform.tfvars, then edit it. Set a distinct naming prefix such as project_name if this will not be the only deployment in the account, subscription, or project.

4

Pin the container image

While editing terraform.tfvars, replace any latest image reference with a tested release tag or an immutable digest. On a database, an unpinned image means the engine version can change underneath a live dataset without a deliberate upgrade.

5

Initialise the working directory

Run tofu init to download the provider plugins declared in versions.tf and wire up the backend configured in step 2.

6

Validate the configuration

Run tofu validate to check the configuration is internally consistent before any provider API is called. This is a syntax and reference check, not a check against your actual cloud account.

7

Review the plan

Run tofu plan and read what it intends to create. This is the last point at which a mis-set variable — an oversized instance type, the wrong region, a missing naming prefix — costs nothing to correct.

8

Apply, then consult the provider README

Run tofu apply to provision the deployment. From there the provider-specific README covers prerequisites, image and license handling, backup and restore, shell or container access, multi-deployment patterns, and cleanup and destroy guidance.

FAQ

Frequently Asked Questions

OpenTofu configurations that provision a single, self-managed Virtuoso Universal Server instance on AWS, Azure, or Google Cloud. You own the resulting infrastructure and the cloud bill; OpenLink owns neither. What you do not get is the managed service — no control plane, no portal, no metering.

OpenTofu is the open-source fork of Terraform that followed Terraform's 2023 licence change, and it remains configuration-compatible: the same HashiCorp Configuration Language files, the same init/validate/plan/apply cycle, the same .tf and .tfvars conventions. Choosing it keeps the deployment path open-source end to end, matching the licence posture of Virtuoso Open Source 7 itself.

The virtual machine baseline, unless you are only running light tests. The repository is unusually specific about why: local SQL benchmarking on 18 August 2026 found active-database latency on the Azure Container Instances variant unacceptable even with Premium Azure Files. That is a measured result about the storage substrate, not a general caution about containers.

All four deployments generate a DBA password, and OpenTofu records generated values in state. A local terraform.tfstate is therefore a plaintext credential file sitting in your working directory. Configure an encrypted remote backend before your first production apply — the repository documents this in docs/REMOTE_STATE.md. On Google Cloud the baseline additionally places the password in Secret Manager, but that does not remove it from state.

Yes. The requirement is separate OpenTofu state per deployment plus a distinct naming prefix such as project_name. Share state between two deployments and each plan will read the other as drift and try to destroy it.

Both are supported where the provider-specific deployment documents them. Image selection and licence handling differ by provider, so the authoritative answer for your target cloud is that provider directory's own README rather than the top-level one.

Because latest makes the database engine version a moving target. A container restart hours or months after deployment could pull a different Virtuoso build against the same on-disk dataset, turning an unplanned engine upgrade into an operational surprise. A tested release tag makes the version explicit; an immutable digest makes it unforgeable.

Not yet. An azure/aks/ variant using AKS with Azure Disk and a gcp/gke/ variant using GKE with Persistent Disk are named as future options under consideration. The current recommended self-managed baselines remain single-instance: Azure VM with Managed Disk, and Compute Engine with Persistent Disk.

So the repository stays readable for its actual audience. An operator who wants to run their own Virtuoso should not have to distinguish deployment code from Marketplace metering, control-plane Lambdas, or portal source. The boundary is stated as an instruction to contributors, not merely as a description of the current contents.

Yes. Five of the six exclusions mark what this repository does not include — it is not the managed service. The sixth is different: state files hold the generated DBA password and terraform.tfvars holds per-deployment configuration, so both are kept out of version control for the same reason any password would be.

Where the operating system lives. On Fargate, AWS runs the container and there is no host for you to patch, log into, or size — at the cost of reaching the database volume only through the file system AWS attaches. On a virtual machine you inherit host upkeep and gain a shell, direct block storage, and the operational access that makes backup, restore, and diagnosis straightforward.

As of 24 August 2026 the public GitHub repository contains README.md only. The layout the README describes exists on the project's own remote at devhub.openlinksw.com, which is not publicly resolvable. Everything in these notes is therefore drawn from the README's prose and layout diagram, not from inspected configuration files.

Through the provider-specific README, which is where the repository deliberately puts prerequisites, supported architecture, image and licence handling, backup and restore workflow, shell or container access, multi-deployment patterns, and cleanup and destroy guidance. The top-level README routes you there rather than duplicating four sets of instructions.

No, and the repository exists partly to make that distinction concrete. Self-managed means the infrastructure, the upgrade schedule, the backups, and the cloud bill are yours. The managed service keeps those with OpenLink and is implemented by a control plane, portals, and Marketplace integration none of which appear here.

Glossary

Glossary of Terms

AWS Fargate

A serverless launch type for Amazon ECS: AWS provisions and runs the container without the operator managing any virtual machine. The compute half of the AWS baseline.

Amazon Elastic File System (EFS)

A managed, elastic NFS file system. It holds the AWS baseline's database volume so a Fargate task carries no state of its own.

Azure Container Instances (ACI)

Azure's serverless container service. The repository's aci/ variant uses it with Azure Files and is marked experimental on the strength of an August 2026 latency benchmark.

Azure Managed Disk

Block storage attached to an Azure virtual machine. Its latency characteristics are the reason the VM baseline, not the container-instance variant, is Azure's recommended route.

Encrypted remote state backend

A remote, encrypted store for OpenTofu state — object storage with server-side encryption, typically with locking. Required before production use here because state carries the generated DBA password.

Google Persistent Disk

Google Cloud's durable block storage, holding the live database volume in the Compute Engine baseline.

Immutable image digest

A content hash identifying one exact container image build. Unlike a tag, it cannot be repointed, so pinning to a digest guarantees the same engine binary on every restart.

Infrastructure as Code (IaC)

The practice of declaring servers, networks, and storage in version-controlled files rather than clicking through a console, so a deployment can be reviewed, reproduced, and destroyed deterministically.

Module

A reusable, parameterised group of resources. Each provider directory here holds a modules/virtuoso-server/ module that its root configuration calls.

Naming prefix (project_name)

A distinct string prepended to a deployment's resource names, so several deployments can coexist in one account, subscription, or project without colliding.

OpenTofu

An open-source infrastructure-as-code tool, forked from Terraform after its 2023 licence change and configuration-compatible with it. The tofu command drives every deployment in this repository.

OpenTofu state

The file OpenTofu keeps mapping declared resources to the real cloud objects it created. It records generated values — including this repository's DBA password — which is why an encrypted remote backend is a hardening requirement rather than a nicety.

Secret Manager

Google Cloud's managed secret store. The GCP baseline is the only one of the four to name a dedicated secret store alongside OpenTofu state.

Self-managed deployment

A deployment where the operator owns the infrastructure, the upgrade cadence, the backups, and the cloud bill. The opposite arrangement — the managed service — is implemented by code this repository deliberately excludes.

Virtuoso Universal Server

OpenLink Software's multi-model database engine and middleware platform, offering SQL, SPARQL, RDF, and GraphQL over a single core. It is the product every baseline provisions.

terraform.tfvars

The per-deployment variable file an operator creates by copying terraform.tfvars.example. It is gitignored by design because it carries deployment-specific and sometimes sensitive configuration.

Knowledge Graph Explorer 202 nodes · 436 links

Interactive graph visualization derived from the companion RDF. Click nodes to resolve, drag to explore. Graph data embedded from companion RDF at generation time.

virtuoso-opentofu — Deployment Graph

Nodes: 0 Links: 0
Click SVG to activate zoom, click outside to release | Drag nodes to pin, double-click to unpin
Classes Properties Instances

SPARQL Workbench 6 sample queries

Query this knowledge graph on URIBurner. The editor opens on the canonical entity-type summary scoped to the DAV named graph. Pick a recipe, edit freely, then run live or copy.

Sample Queries

Reproduced verbatim from the companion RDF. Execute loads the query into the workbench below and runs it live.

Every baseline with its compute and storage substrates and maturity

Reconstructs the provider comparison matrix straight from the graph.

PREFIX schema: <http://schema.org/>
PREFIX : <https://linkeddata.uriburner.com/DAV/demos/daas/virtuoso-opentofu-self-managed-deployments-claude_opus_5-1.html#>
SELECT DISTINCT ?baseline ?compute ?storage ?maturity
FROM <https://linkeddata.uriburner.com/DAV/demos/daas/virtuoso-opentofu-self-managed-deployments-claude_opus_5-1.ttl>
WHERE {
  ?b a :DeploymentBaseline ; schema:name ?baseline ; schema:position ?pos ;
     :usesCompute ?c ; :usesStorage ?s ; :hasMaturity ?m .
  ?c schema:name ?compute . ?s schema:name ?storage . ?m schema:name ?maturity .
}
ORDER BY ?pos
Which baselines put the database volume on a file share rather than block storage?

Isolates the axis the August 2026 benchmark turned into a decision.

PREFIX schema: <http://schema.org/>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
PREFIX dbr: <http://dbpedia.org/resource/>
PREFIX : <https://linkeddata.uriburner.com/DAV/demos/daas/virtuoso-opentofu-self-managed-deployments-claude_opus_5-1.html#>
SELECT DISTINCT ?baseline ?storage ?maturity
FROM <https://linkeddata.uriburner.com/DAV/demos/daas/virtuoso-opentofu-self-managed-deployments-claude_opus_5-1.ttl>
WHERE {
  ?b a :DeploymentBaseline ; schema:name ?baseline ;
     :usesStorage ?s ; :hasMaturity ?m .
  ?s schema:name ?storage ; skos:related dbr:Cloud_storage .
  ?m schema:name ?maturity .
}
The publishing boundary, as data

Lists every artefact class the repository excludes by design.

PREFIX schema: <http://schema.org/>
PREFIX : <https://linkeddata.uriburner.com/DAV/demos/daas/virtuoso-opentofu-self-managed-deployments-claude_opus_5-1.html#>
SELECT DISTINCT ?excluded ?why
FROM <https://linkeddata.uriburner.com/DAV/demos/daas/virtuoso-opentofu-self-managed-deployments-claude_opus_5-1.ttl>
WHERE {
  :repo :excludesArtifactClass ?e .
  ?e schema:name ?excluded ; schema:description ?why ; schema:position ?p .
}
ORDER BY ?p
How many baselines does each cloud provider have, and how many are production-ready?

An aggregation that shows Azure's two-baseline split at a glance.

PREFIX schema: <http://schema.org/>
PREFIX : <https://linkeddata.uriburner.com/DAV/demos/daas/virtuoso-opentofu-self-managed-deployments-claude_opus_5-1.html#>
SELECT ?provider (COUNT(DISTINCT ?b) AS ?baselines) (COUNT(DISTINCT ?avail) AS ?available)
FROM <https://linkeddata.uriburner.com/DAV/demos/daas/virtuoso-opentofu-self-managed-deployments-claude_opus_5-1.ttl>
WHERE {
  ?b a :DeploymentBaseline ; :targetsProvider ?p .
  ?p schema:name ?provider .
  OPTIONAL { ?b :hasMaturity :available . BIND(?b AS ?avail) }
}
GROUP BY ?provider
ORDER BY DESC(?baselines)
Production-hardening constraints and the deployment steps that carry them

Joins each stated constraint to the HowTo step where it has to be satisfied.

PREFIX schema: <http://schema.org/>
PREFIX kpx: <https://thinkingmachines.ai/blog/interaction-models/#>
SELECT DISTINCT ?constraint ?stepPosition ?step
FROM <https://linkeddata.uriburner.com/DAV/demos/daas/virtuoso-opentofu-self-managed-deployments-claude_opus_5-1.ttl>
WHERE {
  ?c a kpx:KeyPrinciple ; schema:name ?constraint .
  OPTIONAL {
    ?s a schema:HowToStep ; schema:subjectOf ?c ;
       schema:name ?step ; schema:position ?stepPosition .
  }
}
ORDER BY ?constraint
Entity-type summary for the whole graph

The canonical starting query: what kinds of thing are in here, how many of each, and one example apiece.

PREFIX schema: <http://schema.org/>
SELECT ?type (COUNT(DISTINCT ?s) AS ?count) (SAMPLE(?s) AS ?example)
FROM <https://linkeddata.uriburner.com/DAV/demos/daas/virtuoso-opentofu-self-managed-deployments-claude_opus_5-1.ttl>
WHERE { ?s a ?type . }
GROUP BY ?type
ORDER BY DESC(?count)
▶ Run live on URIBurner SELECT: text/x-html+tr | DESCRIBE/CONSTRUCT: text/x-html-nice-turtle