Version v0.4.0 of the documentation is no longer actively maintained. The site that you are currently viewing is an archived snapshot. For up-to-date documentation, see the latest version.

SQLite connection

SQLite connection

Connect a task to a sqlite database file. Unlike the SQL-family entries (postgres, mysql, mssql), sqlite has no server — the database is a single file on disk. This page documents what makes the connector different.

URI shape

sqlite:///<absolute file path>

Three slashes total: two for the scheme:// separator and one for the leading / of an absolute path. Examples:

File pathURI
/var/lib/leoflow/warehouse.dbsqlite:///var/lib/leoflow/warehouse.db
/tmp/demo.dbsqlite:///tmp/demo.db
(relative path — avoid)sqlite://warehouse.db ← parses, but urlparse(...).path returns "" for the host-less 2-slash form. Use an absolute path.

The control plane builds this URI from the Connection’s Schema field (which is, by sqlite convention, the file path). There is no host, port, login, or password.

Fields the UI asks for

FieldRequiredNotes
Conn Idyese.g. sqlite_target. Exported as AIRFLOW_CONN_SQLITE_TARGET.
Conn Typeyessqlite.
SchemayesThe absolute path to the database file.
HostnoLeave blank.
LoginnoLeave blank.
PasswordnoLeave blank. There is nothing to encrypt; the cipher gate still applies (#142).
PortnoLeave blank.
ExtraoptionalJSON for connect-time pragmas, e.g. {"timeout":5,"detect_types":"PARSE_DECLTYPES"}. The DAG passes these to sqlite3.connect.

The path-extraction pattern

sqlite3.connect() takes a path string, NOT a URI. The user task must parse the URI itself and pull parsed.path:

import os, sqlite3
from urllib.parse import urlparse

raw = os.environ["AIRFLOW_CONN_SQLITE_TARGET"]
path = urlparse(raw).path
if not path:
    raise ValueError(f"sqlite URI has no path: {raw!r}")
conn = sqlite3.connect(path)

Unlike the SQL family there is no unquote step — paths are not percent-escaped.

Example DAG

examples/sqlite_load uses only Python’s standard-library sqlite3. The example’s README walks through Connection setup and verification.

Lite vs Pro caveats

  • Lite (subprocess) runs the task on the host. The path resolves against the host filesystem — a writable temp dir is fine for demos.
  • Lite (k3d) runs the task in a pod. The path resolves inside the pod; the file disappears when the pod exits unless you mount a hostPath or a PVC.
  • Pro (Kubernetes) same as k3d: persistence requires a volume. For shared state across pods, sqlite is the wrong tool — use a real service (postgres / mysql) instead.

Why no encryption / no password

sqlite’s file-system permissions are the access control. If the task can read the file, it can read the data; if the file mode says 0600 and the file is owned by the running user, only that user can open it. The URI carries no secret, so the Repository’s cipher does not actually encrypt anything for a sqlite Connection — but it must still be configured (the SetConnection gate refuses writes without a cipher; the gate’s purpose is “never store a credential in plaintext”, and for sqlite there is no credential to store).

For at-rest encryption of the sqlite file itself, use SQLCipher (pip install sqlcipher3-binary) and pass the key via Extra. That is out of scope for the cookbook entry but a documented pattern in the SQLCipher project.

Tier 1 integration test

TestSQLiteConnectionURIShapeIntegration (in internal/storage/) covers this entry. It runs on every PR with no service container — sqlite is a library, not a service, so the Tier 1 cost is zero (see #162).

Troubleshooting

SymptomCauseFix
OperationalError: unable to open database filePath missing or unwritableUse an absolute path; ensure the pod / process can write to it.
urlparse(...).path is emptyThe Schema field was left blank, or the URI uses the 2-slash formUse an absolute path in Schema (starts with /).
database is lockedMultiple concurrent writersPick a different DB; sqlite serializes writes by design.
  • ADR 0019 — secret encryption at rest (mostly a no-op for sqlite).
  • ADR 0021 — agent secret delivery.
  • #70 — sqlite connector umbrella.
  • #138 — chain-of-custody contract test (sqlite gets a dedicated test).
  • #162 — tiered integration-test pipeline (sqlite is Tier 1).