Skip to content
Hoody.com

A SQLite database stored in /hoody/databases/ is concurrent-write-safe. Several containers can write to it at the same time without corrupting the file, and the only change to your code is the path.

AI-generated code hits this problem often, because parallel tasks tend to open the same database and race each other. Under /hoody/databases/ the FUSE mount coordinates those writes instead of leaving the calling code to get the locking right.


Database access:

Direct access:


AI agents frequently generate code shaped like this, where parallel tasks share one database file:

# AI-generated code (common pattern)
import sqlite3
# Multiple AI tasks running simultaneously
def process_task(task_id):
conn = sqlite3.connect('/app/data.db') # Same database
cursor = conn.cursor()
cursor.execute("INSERT INTO tasks VALUES (?, 'completed')", (task_id,))
conn.commit()
conn.close()
# Task 1, 2, 3... all running in parallel
# Result: "database is locked" error or corruption

Under /hoody/databases/, the same code runs without race conditions or corruption.

On an ordinary path, two containers writing at once collide:

Terminal window
# Container A writes to database
sqlite3 /app/data.db "INSERT INTO users..."
# Container B writes simultaneously
sqlite3 /app/data.db "INSERT INTO posts..."
# Result: "database is locked" error
# or worse: database corruption

SQLite locks at the file level. When several processes, or several containers, try to write, one is locked out or the database is corrupted.


Store the database in /hoody/databases/ instead:

Terminal window
# Container A
sqlite3 /hoody/databases/shared.db "INSERT INTO users..."
# Container B (simultaneously)
sqlite3 /hoody/databases/shared.db "INSERT INTO posts..."
# Result: Both succeed
# No locking errors
# No corruption
# Zero code changes

/hoody/databases/ is a FUSE mount on the host level that implements concurrent write handling for SQLite databases.


Every container has /hoody/databases/ from the start:

Terminal window
# No setup required - just use it
ls /hoody/databases/
# Directory exists and is ready to use

The directory is present in every container and works immediately. There is nothing to configure and no mount command to run.

┌─────────────────────────────────────────┐
│ Physical Host Server │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Special FUSE Driver Layer │ │
│ │ (concurrent write coordination)│ │
│ └─────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────┐ │
│ │ Actual Database Storage │ │
│ └─────────────────────────────────┘ │
│ │
│ Container A Container B Container C
│ ↓ ↓ ↓
│ /hoody/databases/ /hoody/databases/ ...
└─────────────────────────────────────────┘

The FUSE layer intercepts all writes and coordinates them at the filesystem level, so simultaneous writes do not conflict.


Your application needs no code change beyond the database path:

# Risk of corruption with multiple containers
import sqlite3
conn = sqlite3.connect('/app/database.db')
cursor = conn.cursor()
cursor.execute("INSERT INTO users VALUES (?, ?)", (1, 'Alice'))
conn.commit()

This works with any program that uses sqlite3:

  • Python (sqlite3 module)
  • Node.js (better-sqlite3, sqlite3)
  • Go (mattn/go-sqlite3)
  • PHP (PDO SQLite)
  • Any language with SQLite bindings

One database is reachable over HTTP and through native sqlite3 at the same time:

Terminal window
# Container A: Native sqlite3 (fast, direct access)
sqlite3 /hoody/databases/app.db "SELECT * FROM users"
# Container B: hoody-sqlite HTTP API (remote access)
curl "https://...-sqlite-1.../api/v1/sqlite/db?db=/hoody/databases/app.db" \
-d '{"transaction": [{"query": "SELECT * FROM users"}]}'
# Same database, two access methods

Each path suits different work:

  • Native access for performance-critical operations
  • HTTP access for remote monitoring, web dashboards, API integrations
  • Both methods work simultaneously without conflicts

See: Hoody SQLite for complete HTTP API documentation.


Concurrent write safety:

  • Several containers can write to the same database
  • Several processes inside one container can write
  • No “database is locked” errors
  • No corruption from simultaneous writes

No configuration:

  • Available in every container
  • Nothing to set up or mount
  • Works with the standard sqlite3 library
  • No code change beyond the path

Cross-container databases:

  • One database file shared across many containers
  • Every container sees the same data instantly
  • Suits multi-service architectures
  • Removes the need for a separate database server

Not a replication system:

  • Concurrent writes are coordinated safely
  • Data is not replicated to other hosts
  • Backups do not happen automatically
  • There is no failover

Single host only:

  • Containers on the same server can share databases
  • Containers on different servers cannot, yet
  • Each server has its own /hoody/databases/ space

No automatic backups:

  • Snapshot or copy the databases yourself
  • The FUSE layer gives safety, not redundancy

One application database shared by the frontend, the backend, and the workers:

/hoody/databases/app.db
# Container 1 (API Server)
# Handles user registration, authentication
# Container 2 (Background Worker)
# /hoody/databases/app.db (same database)
# Processes jobs, updates status
# Container 3 (Reporting Dashboard)
# /hoody/databases/app.db (same database)
# Read-only queries for reporting
# All three write safely to the same database

The usual approach is a PostgreSQL or MySQL server that every container reaches over the network, which adds a service to operate and network overhead. Here it is one SQLite file in /hoody/databases/, opened directly by each container.

Two developers write to one database during active development:

Terminal window
# Developer A's container writes schema changes
sqlite3 /hoody/databases/dev.db < migrations/001.sql
# Developer B's container writes seed data (simultaneously)
sqlite3 /hoody/databases/dev.db < seeds/users.sql
# No conflicts - both succeed

Ingestion and real-time queries run against the same database:

Terminal window
# Container A: Ingest metrics
while true; do
sqlite3 /hoody/databases/metrics.db \
"INSERT INTO events VALUES (datetime('now'), '$data')"
done
# Container B: Query metrics (simultaneously)
sqlite3 /hoody/databases/metrics.db \
"SELECT COUNT(*) FROM events WHERE timestamp > datetime('now', '-1 hour')"
# No blocking - queries run while inserts happen

The same databases are reachable over HTTP through hoody-sqlite.

hoody CLI prerequisite. The CLI tabs below invoke hoody db …. If you haven’t already, install the CLI (npm i -g hoody-sdk) and authenticate (hoody auth login) so the CLI picks up your Hoody token and default base URL.

Terminal window
# Execute SQL transaction via CLI
hoody db exec-transaction -c $CONTAINER --db /hoody/databases/app.db \
--transaction '[{"query": "SELECT * FROM logs ORDER BY timestamp DESC LIMIT 100"}]'
# Create a new database
hoody db create -c $CONTAINER --path /hoody/databases/analytics.db
Terminal window
# Set a key-value pair
hoody kv set user:1 -c $CONTAINER --db /hoody/databases/app.db \
--body '{"name": "Alice", "email": "alice@example.com"}'
# Get a value by key
hoody kv get user:1 -c $CONTAINER --db /hoody/databases/app.db
// Backend container: Native sqlite3 for bulk inserts (faster)
const db = require('better-sqlite3')('/hoody/databases/app.db');
db.prepare('INSERT INTO logs VALUES (?, ?)').run(timestamp, message);
// Frontend container: HTTP API for remote queries. No Authorization header here
// because this container has no proxy permissions configured (the default).
// Add the credential its policy requires once you configure one.
const response = await fetch(
'https://...-sqlite-1.../api/v1/sqlite/db?db=/hoody/databases/app.db',
{
method: 'POST',
body: JSON.stringify({
transaction: [{
query: 'SELECT * FROM logs ORDER BY timestamp DESC LIMIT 100'
}]
})
}
);

Writes stay on the fast native path while reads remain available over HTTP.

See: Hoody SQLite KV Store and SQL Operations


Keep SQLite databases in /hoody/databases/

Section titled “Keep SQLite databases in /hoody/databases/”

A database that more than one container will open must never live outside this directory:

Terminal window
# Correct - concurrent write safe
/hoody/databases/production.db
/hoody/databases/cache.db
/hoody/databases/analytics.db
# Wrong - risk of corruption
/hoody/storage/production.db
/var/lib/myapp/data.db
/tmp/cache.db

No special driver is needed:

# Works with standard library
import sqlite3
conn = sqlite3.connect('/hoody/databases/app.db')

The FUSE mount handles the concurrent-write safety, so your code stays standard.

Concurrent-write safety is not a backup:

Terminal window
# Via snapshot (captures entire container state)
POST /api/v1/containers/{id}/snapshots
{"alias": "before-migration"}
# Or copy database file
cp /hoody/databases/production.db /hoody/storage/backups/prod-2025-11-10.db
# Or use sqlite3 backup command
sqlite3 /hoody/databases/production.db ".backup /hoody/storage/backups/backup.db"
Terminal window
# Check database sizes
du -sh /hoody/databases/*
# Vacuum to compact
sqlite3 /hoody/databases/app.db "VACUUM"
# Set up auto-vacuum
sqlite3 /hoody/databases/app.db "PRAGMA auto_vacuum = FULL"

Concurrent writes are safe, but query speed still depends on indexes:

-- Create indexes for frequently queried columns
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_posts_author ON posts(author_id, created_at);
-- Analyze for query planner
ANALYZE;

Do I need to do anything to enable /hoody/databases/?

Section titled “Do I need to do anything to enable /hoody/databases/?”

No. The directory is available in every container. Start using it:

Terminal window
sqlite3 /hoody/databases/myapp.db "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)"

Can containers on different servers share databases?

Section titled “Can containers on different servers share databases?”

Not yet. /hoody/databases/ is host-level, so sharing works between containers on the same physical server.

Across servers, use the hoody-sqlite HTTP API to reach a database remotely.

No. The FUSE mount handles SQLite’s file-level locking protocol and nothing else. Run PostgreSQL or MySQL as services in containers and connect to them over the network.

What if my database gets really large (>10GB)?

Section titled “What if my database gets really large (>10GB)?”

The FUSE mount works regardless of database size, but keep databases under 100GB for the best performance. For larger datasets, consider:

  • Sharding across multiple databases
  • Indexing aggressively
  • Moving to a container with more storage
  • Running a dedicated PostgreSQL container

Can I use WAL mode with /hoody/databases/?

Section titled “Can I use WAL mode with /hoody/databases/?”

Yes. SQLite’s Write-Ahead Logging (WAL) mode works with the concurrent-write FUSE mount:

PRAGMA journal_mode = WAL;

WAL adds concurrent read performance on top of the mount’s concurrent write safety.

Does the HTTP API require /hoody/databases/?

Section titled “Does the HTTP API require /hoody/databases/?”

No. hoody-sqlite can access databases anywhere in the container filesystem:

Terminal window
# Works - in /hoody/databases/
curl "...?db=/hoody/databases/app.db"
# Also works - anywhere else
curl "...?db=/home/user/data/test.db"

Concurrent write safety across containers is the exception: for that, the database must be in /hoody/databases/.


Lock errors can still appear while using /hoody/databases/. The usual causes:

  1. Long-running transactions:

    -- Wrong: Holding write lock too long
    BEGIN EXCLUSIVE;
    -- Complex operations taking seconds
    COMMIT;
    -- Correct: Break into smaller transactions
    BEGIN; INSERT ...; COMMIT;
    BEGIN; INSERT ...; COMMIT;
  2. Busy timeout too low:

    # Increase timeout
    conn = sqlite3.connect('/hoody/databases/app.db')
    conn.execute('PRAGMA busy_timeout = 5000') # 5 seconds
  3. Very high write concurrency:

    • The FUSE mount has limits
    • Consider connection pooling
    • Or switch to WAL mode

For sqlite3: cannot open database, check the directory, create the database if it is missing, and check permissions:

Terminal window
# Verify directory exists
ls -la /hoody/databases/
# Create database if needed
sqlite3 /hoody/databases/newdb.db "CREATE TABLE test (id INTEGER)"
# Check permissions
ls -la /hoody/databases/newdb.db
# Should be: -rw-r--r-- root root

If queries run slower in /hoody/databases/ than on the regular filesystem, work through these:

  1. Enable WAL mode:

    PRAGMA journal_mode = WAL;
    PRAGMA synchronous = NORMAL;
  2. Use indexes:

    CREATE INDEX idx_query ON table(column);
    ANALYZE;
  3. Increase cache size:

    PRAGMA cache_size = -64000; -- 64MB cache
  4. Batch operations:

    BEGIN;
    -- Multiple INSERTs
    COMMIT;

The FUSE mount coordinates writes over time like this:

Time Container A FUSE Layer Container B
─────────────────────────────────────────────────────────────────────
T1 BEGIN TRANSACTION ← Request write lock (waiting)
T2 INSERT INTO users Lock granted to A (waiting)
T3 INSERT INTO posts Buffering A's writes (waiting)
T4 COMMIT Flushing A's changes (waiting)
T5 (done) Release lock BEGIN TRANSACTION
T6 (ready for next) Grant lock to B ← INSERT INTO logs
T7 Buffering B's writes COMMIT
T8 Release lock (done)

The FUSE layer serializes competing writes while allowing concurrent reads.


Storage:

Database access: