This package allows you to use Litestream's continuous backup capabilities "in-process" with your restinpieces application. It removes the need to manage a separate Litestream binary, systemd service, and standalone configuration files.
Instead, it provides a restinpieces daemon that is compiled into your application binary and integrates directly with the framework's lifecycle and secure configuration store.
This section provides a step-by-step guide to get the example application running and see the backup/restore process in action. The example application itself can be found in cmd/example/main.go.
This package integrates with an existing restinpieces application. Before you begin, you must initialize the main application database (app.db) with the necessary tables.
You can do this using the ripc command-line tool from the restinpieces framework.
# This command creates a new app.db file with the required framework tables.
ripc -dbpath app.db app createFirst, you need to provide a Litestream configuration file. This package uses the standard litestream.yml format.
-
Create
litestream.yml: Create alitestream.ymlfile with your desired backup replica settings. For example, to back up to a local directory:dbs: - path: ./app.db replicas: - type: file path: ./litestream-replicas
For more details and other replica types like S3, see the official Litestream configuration documentation.
-
Store the Configuration: The
restinpiecesframework stores all configuration securely inside the application database. Use theripccommand-line tool (provided by therestinpiecesproject) to encrypt and save yourlitestream.yml.# This command assumes you have an age key and the ripc tool. ripc -age-key age_key.txt -dbpath app.db config save -scope litestream litestream.ymlThis command saves the configuration under the
litestreamscope, which the daemon will use to load its settings on startup.
This repository includes an example application that demonstrates how to integrate the Litestream daemon.
Build the application using the standard Go toolchain:
go build -o myapp ./cmd/exampleThis compiles the code in cmd/example and creates an executable file named myapp.
Now, run the compiled application, providing the same age_key.txt and dbpath you used to save the configuration:
./myapp -age-key age_key.txt -dbpath app.dbThis will start the main web server and, alongside it, the Litestream daemon will begin monitoring app.db and replicating any changes to your configured destination. You should see log output from both the application and Litestream indicating that they have started.
While the app is running, you can make changes to the database (e.g., by using sqlite3 app.db "CREATE TABLE t(id INT);") and you will see Litestream backing them up.
Note on Privileged Ports: If your application is configured to listen on a privileged port (any port below 1024, like 80 or 443), you will need to grant the executable special capabilities to bind to it without running as root. You can do this with the setcap command:
sudo setcap cap_net_bind_service=+ep ./myappTo simulate a disaster recovery scenario, you can use the official litestream binary to restore the database from your replica. This is useful for migrating a database, recovering from hardware failure, or testing your backup integrity.
-
Install Litestream: If you don't have it, install the Litestream binary.
-
Stop your application: Stop the running
./myappprocess (Ctrl+C). You can delete the local database files (e.g.,app.db,app.db-wal) to simulate a complete data loss. -
Restore the database: The restore command depends on how your
litestream.ymlis configured.Scenario A: Simple Single Database
If your
litestream.ymlspecifies a single database with apathfield, you can restore it by pointing to the config and specifying the database path.# Given a litestream.yml that contains: # dbs: # - path: ./app.db # replicas: ... # Restore command: litestream restore -o app.db -config litestream.yml ./app.db
This tells Litestream to find the configuration for
./app.dbin thelitestream.ymlfile and restore it to a new file namedapp.db.Scenario B: Directory Monitoring
If you are using Litestream v0.5.0's directory monitoring feature, your
litestream.ymlspecifies adirinstead of apath.# Given a litestream.yml that contains: # dbs: # - dir: ./data/ # replicas: ...
In this case, Litestream is backing up every database inside the
./data/directory. To restore, you must specify the exact path of the database you want to recover. For example, to restore a database namedcustomer1.dbthat was in that directory:# Restore a specific DB from the monitored directory litestream restore -o customer1.restored.db -config litestream.yml ./data/customer1.dbThis command looks at
litestream.yml, finds the configuration for the./data/directory, and then uses that to find the correct replica for./data/customer1.dband restores it tocustomer1.restored.db.
After the restore is complete, you can restart your application, and it will have all its data back.
This package uses the standard litestream.yml configuration format. Litestream configuration is managed securely through the restinpieces SecureConfigStore, as shown in the Quickstart guide.
For more information on ripc, see the ripc documentation.
Note: No Environment Variables
A key principle of the restinpieces framework is that all configuration must be self-contained and securely stored in the database to create a single, auditable source of truth. Therefore, this package does not support environment variable expansion (e.g., $HOME or ${VAR}) within the litestream.yml file.
To enforce this, a validation check runs on startup. The presence of environment variable syntax will cause the application to fail, even if the variables are inside comments. Please ensure your configuration contains only explicit paths and values.
The upstream Litestream project includes several features designed for when it is run as a standalone binary. When using restinpieces-litestream as an embedded Go library, some of these features are not suitable and have been deactivated. Specifically, configuration options related to the exec subcommand, the MCP server, and Prometheus metrics are not supported.
The upstream Litestream project is designed primarily as a standalone binary. This architecture makes it difficult to cleanly inject a custom slog.Logger when using Litestream as an embedded library, as its internal components fall back to a global default logger.
To solve this without requiring a heavily modified and hard-to-maintain fork, this package uses a "split-logging" model. Our compromise is to expose Litestream's own logging configuration, allowing us to control its output separately from the main framework logger.
This package follows a "split-logging" model:
-
Framework Logs: All logs generated by the
restinpieces-litestreamdaemon wrapper itself are sent to the main framework'sslog.Loggerinstance. If you are using the defaultrestinpiecessetup, these logs will be written to the SQLite database with full structured context. -
Internal Litestream Logs: All internal logs from the core Litestream components (e.g., relating to replication, snapshots, compaction) are sent to either
os.Stdout(the default) oros.Stderr. This behavior mimics running Litestream as a separate binary alongside the main application.
The destination, format, and level of these internal logs can be controlled via the litestream.yml configuration file. This is handled by a function from our Litestream fork that configures the library's internal global logger.
Example litestream.yml logging section:
logging:
# 'debug', 'info', 'warn', or 'error'
level: 'info'
# 'text' or 'json'
type: 'text'
# Direct logs to stderr instead of stdout. Defaults to false.
stderr: falseThis configuration would cause the internal Litestream logs to be written to os.Stdout as text at the INFO level, while the main framework logs continue to go to their own destination.
As of v0.5.0, the underlying Litestream library uses the excellent modernc.org/sqlite driver, which is pure-Go.
This is a major advantage as it means this package has no CGO dependency.
- No C compiler or external dependencies are needed to build your application.
- Cross-compilation is simple.
- There are no conflicts with any other SQLite drivers (whether pure-Go or CGO-based) that your main application might use.
This aligns perfectly with the restinpieces framework, whose default database driver is zombiezen.com/go/sqlite, which is also a pure-Go driver. This ensures a CGO-free environment when using the default settings for both the framework and this package.
Consider setting the following PRAGMAs in your application when initializing the database connection for optimal performance and compatibility with Litestream:
Disable autocheckpoints for high write load servers. Prevent aplication to do checkpoints:
PRAGMA wal_autocheckpoint = 0;
This mode will ensure that the fsync() calls are only called when the WAL becomes full and has to checkpoint to the main database file. This is safe as the WAL file is append only. Should be probably default when using WAL:
PRAGMA synchronous = NORMAL;
Litestream requires periodic but short write locks on the database when checkpointing occurs. SQLite will return an error by default if your application tries to obtain a write lock at the same time. This pragma will wait up to a given number of milliseconds before failing a query if it is blocked on a write.
PRAGMA busy_timeout = 5000;