SQLite
A Conn3x circuit that installs the sqlite3 command from each distribution’s packages, and can create empty databases with the owner and mode you want, ready for your application to open.
Be clear on one thing before you use it. The SQLite version differs per distribution, because the distributions ship it and this circuit takes what they ship:
| Distribution | SQLite, checked October 2026 |
|---|---|
| Ubuntu 24.04 | 3.45.1 |
| Debian 13 | 3.46.1 |
| Rocky Linux 10 | 3.46.1 |
| Fedora 45 | 3.53.4 |
The file format hasn’t changed across these, so a database moves between them. A newer SQL feature might not.
module "sqlite" { source = "github.com/conn3xorg/circuit-sqlite?ref=v0.1.2"
hosts = { app1 = "203.0.113.10", app2 = "203.0.113.11" } ssh_private_key_file = "~/.ssh/id_ed25519"
databases = { "/var/lib/app/app.db" = { owner = "app", mode = "0640" } }}Then run conn3x init and conn3x apply. A second conn3x plan says No changes.
For hosts created in the same plan, pass host objects with the host key you generated, so Conn3x checks it without a known_hosts entry:
hosts = { for name, d in digitalocean_droplet.app : name => { address = d.ipv4_address host_key = tls_private_key.host[name].public_key_openssh }}To try it on a throwaway container first, see examples/local.
Databases
Section titled “Databases”A database that’s already there keeps its contents, every time: the circuit only ever sets its owner and mode. SQLite writes a journal beside the database, so the owner needs to be able to write to the directory too. When the directory doesn’t exist, the circuit creates it for the owner, with mode 0750. When it does, it’s left alone, so you can’t take over /var/lib by accident, and write access to it is yours to arrange.
Root only does this work where no other account can change any part of the path. Anywhere else, such as the owner’s own directory, it’s done as the owner, with only the owner’s rights. That has two effects you might notice. A database in a directory the owner can write and that another user owns stops with an error until you hand it over with chown once. And owner = "root", the default, stops with an error in a directory another account can change, since nothing there could be trusted to stay root’s.
The owner has to exist already. Create the user with your own step or circuit first.
What it does
Section titled “What it does”On each host, in order:
- Installs
sqlite3on Debian and Ubuntu, orsqliteon the Red Hat family. - For each of
databases, creates its directory if it’s missing, creates the database if it’s missing or empty, and sets its owner and mode, unless they’re already right.
Before the circuit runs, the conn3x_host data source reads each host’s distribution over SSH, which picks the package.
What it doesn’t do
Section titled “What it doesn’t do”- It doesn’t create tables or run SQL. The schema belongs to your application.
- It doesn’t back anything up.
- It doesn’t pin SQLite. Updates arrive with the host’s other package updates.
- It doesn’t uninstall anything. A database you take out of
databases, and everything else when you delete the module, stays on the host. It never deletes a database.
Inputs
Section titled “Inputs”| Name | Type | Default | Description |
|---|---|---|---|
hosts |
any | Required | Hosts to configure, by name. Each value is an address, such as "203.0.113.10", or a host object such as { address = "203.0.113.10", host_key = "ssh-ed25519 AAAA..." }. A host object can also set user, port, private_key_file and jump_hosts. The two forms can be mixed. |
ssh_user |
string | "root" |
User to log in as. |
ssh_port |
number | 22 |
SSH port. |
ssh_private_key_file |
string | null |
Path to a private key. When null, Conn3x uses the SSH agent. |
ssh_known_hosts_file |
string | null |
known_hosts file to check host keys against. When null, ~/.ssh/known_hosts. Not used for a host object with host_key. |
ssh_become |
bool | false |
Run commands as root through sudo. Set it when ssh_user isn’t root. |
databases |
map(object) | {} |
Empty databases to create, by absolute path. Each object takes owner (default "root"), group (default the owner’s name) and mode (default "0640"). |
Outputs
Section titled “Outputs”| Name | Description |
|---|---|
sqlite3 |
Path of the sqlite3 command on each host, by host name: /usr/bin/sqlite3. |
Requirements
Section titled “Requirements”Requires Conn3x v0.6.10 or later, and a package mirror each host can reach. amd64 and arm64.
Tested on
Section titled “Tested on”Rocky Linux 10, Fedora 45, Ubuntu 24.04 and Debian 13. examples/local was applied to each on arm64, and the second plan had no changes. The database’s owner then created a table and wrote to it, root read the row back, the owner and mode hadn’t moved, and one more plan had no changes. CI runs the same check on amd64, and checks that a link at a database’s path leaves what it points to unchanged.
On an Ubuntu 24.04 droplet and a Rocky Linux 10 droplet with SELinux enforcing, both s-1vcpu-1gb, amd64, each shared with the other circuits of this batch: the same check passed, and after a reboot the plan had no changes and the data was still there.
ssh_become wasn’t exercised: every test logged in as root.
License
Section titled “License”MPL-2.0. See LICENSE. SQLite is in the public domain.
Source
Section titled “Source”- Repository: conn3xorg/circuit-sqlite. This page covers v0.1.2; the repository’s tags list every release.
- Examples: examples/local.
- Related circuits: PostgreSQL, MariaDB, MySQL, SQL Server, CouchDB, Valkey, OpenSearch.