Skip to content

Configuration Reference: Server, Database, Storage

Stowry uses a YAML configuration file with environment variable overrides.

Configuration values are loaded in this order (later sources override earlier ones):

  1. Default values
  2. Configuration file (config.yaml)
  3. Environment variables (STOWRY_ prefix)
  4. Command-line flags
# Server configuration
server:
port: 5708 # HTTP server port (default: 5708)
mode: store # Server mode: store, static, spa (default: store)
max_upload_size: 0 # Max upload size in bytes, 0 = unlimited (default: 0)
error_document: "" # Custom 404 page path for static mode (default: built-in)
# Service configuration
service:
cleanup_timeout: 30s # Cleanup operation timeout (default: 30s)
# Database configuration
database:
type: sqlite # Database type: sqlite, postgres (default: sqlite)
dsn: stowry.db # Connection string, file path, or :memory: (default: :memory:)
tables:
meta_data: stowry_metadata # Metadata table name (default: stowry_metadata)
# Storage configuration
storage:
path: ./data # Directory for file storage, or :memory: (default: :memory:)
# Server the client commands talk to
endpoint: http://localhost:5708
# Authentication configuration
auth:
read: public # Read access: public, private (default: public)
write: public # Write access: public, private (default: public)
aws:
region: us-east-1 # AWS region for signature verification
service: s3 # AWS service name (default: s3)
access_key: AKIAIOSFODNN7EXAMPLE
secret_key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
keys:
# file: /path/to/keys.json # Several pairs
# Logging
log:
level: info # Log level: debug, info, warn, error (default: info)
Option Type Default Description
port int 5708 HTTP server port
mode string store Server mode (store, static, spa)
max_upload_size int 0 Maximum upload size in bytes (0 = unlimited)
error_document string "" Custom 404 page path for static/SPA modes (empty = built-in HTML)
Option Type Default Description
cleanup_timeout duration 30s Cleanup operation timeout. A bare number is nanoseconds, so write 30s.
Option Type Default Description
type string sqlite Database type (sqlite requires 3.24+, postgres)
dsn string :memory: Connection string, file path, or :memory:
migrate bool false Create the schema before the command does its own work
tables.meta_data string stowry_metadata Metadata table name (lowercase, alphanumeric with underscores, max 63 chars)

Migration Options:

  1. CLI migration (recommended): Run stowry migrate before starting the server
  2. Manual SQL: Execute the schema SQL below directly in your database
-- Table name must be lowercase, alphanumeric with underscores, max 63 chars
CREATE TABLE IF NOT EXISTS stowry_metadata (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
path TEXT NOT NULL UNIQUE,
content_type TEXT NOT NULL,
etag TEXT NOT NULL,
file_size_bytes BIGINT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ,
cleaned_up_at TIMESTAMPTZ
);
CREATE INDEX IF NOT EXISTS idx_stowry_metadata_deleted_at
ON stowry_metadata (deleted_at)
WHERE (deleted_at IS NOT NULL);
CREATE INDEX IF NOT EXISTS idx_stowry_metadata_pending_cleanup
ON stowry_metadata (deleted_at, cleaned_up_at)
WHERE (deleted_at IS NOT NULL AND cleaned_up_at IS NULL);
CREATE INDEX IF NOT EXISTS idx_stowry_metadata_active_list
ON stowry_metadata (created_at, path)
WHERE (deleted_at IS NULL);
-- Table name must be lowercase, alphanumeric with underscores, max 63 chars
CREATE TABLE IF NOT EXISTS stowry_metadata (
id TEXT NOT NULL PRIMARY KEY,
path TEXT NOT NULL UNIQUE,
content_type TEXT NOT NULL,
etag TEXT NOT NULL,
file_size_bytes INTEGER NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
deleted_at TEXT,
cleaned_up_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_stowry_metadata_deleted_at
ON stowry_metadata (deleted_at);
CREATE INDEX IF NOT EXISTS idx_stowry_metadata_pending_cleanup
ON stowry_metadata (deleted_at, cleaned_up_at);
CREATE INDEX IF NOT EXISTS idx_stowry_metadata_active_list
ON stowry_metadata (created_at, path);

SQLite DSN Examples:

dsn: stowry.db # File-based database
dsn: :memory: # In-memory (testing only)
dsn: /var/lib/stowry/db # Absolute path

PostgreSQL DSN Examples:

dsn: postgres://user:password@localhost:5432/stowry
dsn: postgres://user:password@localhost:5432/stowry?sslmode=disable
dsn: host=localhost port=5432 user=stowry password=secret dbname=stowry
Option Type Default Description
path string :memory: Directory for file storage, or :memory:
populate bool false Record the files already under path before the server starts

The storage directory is created automatically with 0o700 permissions (owner-only). Files are organized by their path, keeping the original directory structure. A deployment where another container needs read access should pre-create the directory with 0o750.

Option Type Default Description
read string public Read access mode (public, private)
write string public Write access mode (public, private)
aws.region string us-east-1 AWS region for signature verification
aws.service string s3 AWS service name
access_key string - Access key for signed requests
secret_key string - Secret for that access key
keys.file string - Path to a JSON file of key pairs, for more than one

Note: The access control matrix below applies to store mode only. In static and spa modes, all access is public and write operations return 405 Method Not Allowed.

Access Control Matrix:

read write GET PUT DELETE
public public Public Public Public
public private Public Auth required Auth required
private private Auth required Auth required Auth required
private public Auth required Public Public

The config carries one key pair. Either half alone is rejected at startup.

auth:
read: private
write: private
access_key: KEY1
secret_key: SECRET1

Several keys, from a file:

auth:
keys:
file: /path/to/keys.json

Where keys.json contains:

[
{"access_key": "KEY1", "secret_key": "SECRET1"},
{"access_key": "KEY2", "secret_key": "SECRET2"}
]
Option Type Default Description
level string info Minimum log level

Log levels: debug, info, warn, error

All configuration options can be set via environment variables using the STOWRY_ prefix with underscores replacing dots.

Config Path Environment Variable
server.port STOWRY_SERVER_PORT
server.mode STOWRY_SERVER_MODE
server.max_upload_size STOWRY_SERVER_MAX_UPLOAD_SIZE
service.cleanup_timeout STOWRY_SERVICE_CLEANUP_TIMEOUT
database.migrate STOWRY_DATABASE_MIGRATE
storage.populate STOWRY_STORAGE_POPULATE
cors.allowed_origins STOWRY_CORS_ALLOWED_ORIGINS
cors.allowed_methods STOWRY_CORS_ALLOWED_METHODS
cors.allowed_headers STOWRY_CORS_ALLOWED_HEADERS
cors.exposed_headers STOWRY_CORS_EXPOSED_HEADERS
cors.allow_credentials STOWRY_CORS_ALLOW_CREDENTIALS
cors.max_age STOWRY_CORS_MAX_AGE
database.type STOWRY_DATABASE_TYPE
database.dsn STOWRY_DATABASE_DSN
database.tables.meta_data STOWRY_DATABASE_TABLES_META_DATA
storage.path STOWRY_STORAGE_PATH
auth.read STOWRY_AUTH_READ
auth.write STOWRY_AUTH_WRITE
auth.aws.region STOWRY_AUTH_AWS_REGION
auth.aws.service STOWRY_AUTH_AWS_SERVICE
auth.keys.file STOWRY_AUTH_KEYS_FILE
auth.access_key STOWRY_ACCESS_KEY
auth.secret_key STOWRY_SECRET_KEY
endpoint STOWRY_ENDPOINT
log.level STOWRY_LOG_LEVEL

The credentials are the exception to the STOWRY_ + path rule, dropping the section name. Each setting answers to exactly one variable, so STOWRY_AUTH_ACCESS_KEY is not read.

Example:

Terminal window
STOWRY_SERVER_PORT=8080 \
STOWRY_DATABASE_TYPE=postgres \
STOWRY_DATABASE_DSN="postgres://localhost/stowry" \
./stowry serve

Every setting has a flag. stowry --help lists them all with the environment variable each one mirrors; the ones reached for most often:

Terminal window
-c, --config strings Config file paths, merged left to right
--db-type string Database type (sqlite, postgres)
-d, --db-dsn string Database connection string
-s, --storage-path string Storage directory path
-a, --access-key string Access key for signed requests
-k, --secret-key string Secret key for signed requests
-e, --endpoint string Server the client commands talk to
--log-level string debug, info, warn or error

serve adds --port, --mode, -m/--migrate and -p/--populate.

Example:

Terminal window
./stowry serve --config /etc/stowry/config.yaml --db-type postgres
server:
port: 5708
mode: store
database:
type: sqlite
dsn: dev.db
tables:
meta_data: stowry_metadata
storage:
path: ./uploads
auth:
read: public
write: public
log:
level: debug
server:
port: 5708
mode: store
max_upload_size: 104857600 # 100MB upload limit
service:
cleanup_timeout: 30s
database:
type: postgres
# No shell expansion here; set STOWRY_DATABASE_DSN to keep the password out of the file
dsn: postgres://stowry:CHANGEME@db.example.com:5432/stowry?sslmode=require
tables:
meta_data: stowry_metadata
storage:
path: /var/lib/stowry/data
auth:
read: private
write: private
aws:
region: us-east-1
service: s3
# Credentials come from STOWRY_ACCESS_KEY and STOWRY_SECRET_KEY
log:
level: info
server:
port: 80
mode: static
database:
type: sqlite
dsn: metadata.db
tables:
meta_data: stowry_metadata
storage:
path: /var/www/html
auth:
read: public
write: public # ignored in static mode; auth.read: private would fail at startup
server:
port: 3000
mode: spa
database:
type: sqlite
dsn: app.db
tables:
meta_data: stowry_metadata
storage:
path: ./dist
auth:
read: public
write: public # ignored in spa mode; auth.read: private would fail at startup