Every app includes:
| path | use |
|---|---|
/shared.db |
replicated SQLite |
/shared/ |
replicated files |
Open them as local paths. Writes commit locally, converge across instances and regions, and land durably in object storage. There is no database service or primary endpoint to provision.
Use these before adding a volume. A persistent volume is for software that requires a private local filesystem.
sqlite3 /shared.db <<'SQL'
CREATE TABLE IF NOT EXISTS notes(id TEXT PRIMARY KEY, body TEXT);
INSERT INTO notes VALUES('one', 'hello')
ON CONFLICT(id) DO UPDATE SET body=excluded.body;
SELECT body FROM notes WHERE id='one';
SQL
printf 'generated once\n' > /shared/artifact.txt
cat /shared/artifact.txt
multi-writer rules
- Replicated tables need a primary key. Bare
INTEGER PRIMARY KEYandAUTOINCREMENTschemas are rewritten to assign sparse, collision-safe 63-bit values. Recover them withINSERT ... RETURNING id, notlast_insert_rowid(). - An instance reads its own commit immediately. Other replicas converge asynchronously, normally in under a second.
- Framework migrations work when each transaction contains one DDL statement plus bookkeeping. Split transactions containing several DDL statements.
- Tables whose names begin with
_stay local by default. Setpersistence.replicate-underscore-tables=truewhen creating the database to include them in replication; the database retains that policy afterward. - Temporary tables and
:memory:databases remain local.
SQLite compatibility
/shared.db works transparently with programs that dynamically link the
system libsqlite3: Python's stdlib, Ruby's system SQLite gem, the CLI, Rails,
Django, and Go built with -tags=libsqlite3.
Bindings that statically embed SQLite cannot be intercepted. Examples include
better-sqlite3, Node's built-in node:sqlite, modernc.org/sqlite, and
mattn/go-sqlite3 without the system-library build tag. Rebuild against
libsqlite3.so, or load the engine explicitly after opening:
SELECT load_extension('/usr/local/lib/syzy-engine.so', 'sqlite3_syzy_init');
shared files
/shared/ is a content-oriented POSIX mount for uploads, generated assets, and
small blobs. Advisory locks, mmap, and O_DIRECT are not supported; put
coordination in /shared.db.
mount shared files elsewhere
A shared Compose volume mounts the app's /shared/ tree at another path:
services:
web:
volumes: [uploads:/srv/uploads]
worker:
volumes: [uploads:/work/uploads]
volumes:
uploads:
x-kedge: {shared: true}
Both mount points refer to the same replicated file tree. One service may currently mount one shared volume path.
named databases
An app's default database follows that app. A named database can outlive and serve several apps:
kedge db create mydata
kedge db attach mydata myapp
kedge db attach mydata myworker
New instances attach immediately; existing instances pick it up when they
recycle. kedge db detach myapp returns that app to its private default. The
app page provides a SQL console for either kind.
previews and forks
Previews get isolated shared data. Forking branches the source database and files at one instant without copying all pages; unchanged content remains shared underneath, while later writes stay on their own branch.
kedge fork myapp myapp-experiment