Example repository to develop scaffolding for using Bun + SQLite with deployment on Railway.
If running you can find it:
- at railway: https://example-bun-sqlite-railway-production.up.railway.app/
- at fly.io: https://example-bun-sqlite-hosting.fly.dev
- at sevalla: https://example-bun-sqlite-hosting-sc13n.sevalla.app/todos
- Pure Bun - No external frameworks, uses Bun.serve and built-in SQLite
- TypeScript Throughout - Both frontend and backend use TypeScript
- RESTful API - Complete CRUD operations for todos
- Persistent Storage - SQLite database with proper Railway volume setup
- Production Ready - Health checks, restart policies, and proper error handling
- Runtime: Bun
- Database: SQLite (via
bun:sqlite) - Frontend: Vanilla HTML + TypeScript
- Backend: Bun.serve with TypeScript
- Deployments:
- Railway with Volume storage
- Fly.io with Volume storage
.
├── src/
│ ├── index.ts # Main server + SQLite setup + API routes
│ ├── client/ # Frontend files
│ │ ├── about.html # About page
│ │ ├── todos.html # Todo app page
│ │ └── assets/
│ │ ├── todos.ts # Frontend TypeScript
│ │ └── styles.css # Minimal styling
│ └── db.ts # Database setup and queries
├── data/ # SQLite storage (gitignored)
│ └── .keep
├── railway.toml # Railway deployment config
├── fly.toml # Fly.io deployment config
├── package.json # Scripts and dependencies
└── README.md
GET /- Redirects to/todosGET /todos- Todo list pageGET /about- About pageGET /healthz- Health check endpointGET /api/todos- List all todosPOST /api/todos- Create a new todoGET /api/todos/:id- Get a single todoPATCH /api/todos/:id- Update a todoDELETE /api/todos/:id- Delete a todo
- Bun installed
- Clone the repository
- Install dependencies:
bun install- Start the development server with hot reload:
bun run devThe SQLite database will be created at data/app.sqlite.
# Development with hot reload
bun run dev
# Development with file watching
bun run dev-watch
# Production mode
bun run start
# Build for production
bun run build
# Railway setup (requires Railway CLI)
bun run railway:setup # Setup volume + env var
bun run railway:volume # Add volume only
bun run railway:env # Set env var only- GitHub account
- Railway account
- Railway CLI (optional, for automated setup)
-
Install Railway CLI
# macOS/Linux curl -fsSL https://railway.app/install.sh | sh # Or with npm npm i -g @railway/cli
-
Login & Link Project
railway login railway link # Link to existing project or create new one -
Setup Volume & Environment (One Command)
bun run railway:setup
Or run individually:
# Add volume bun run railway:volume # Set environment variable bun run railway:env
-
Deploy (optional via CLI)
railway up
PORT- Set automatically by RailwayDB_PATH- Path to SQLite database (set to/data/app.sqliteon Railway)
The railway.toml file configures:
- Builder: Nixpacks (auto-detects Bun)
- Start Command:
bun src/index.ts - Health Check:
/healthzendpoint with 10s timeout - Restart Policy: ON_FAILURE with max 3 retries
- machine setup: see "systems/server/paas2" - running on hetzner
- Setup a volume: "uc volume create example-bun-sqlite-hosting_data"
Use command "uc deploy": uc deploy
❯ uc deploy --help
Deploy services from a Compose file.
Usage:
uc deploy [FLAGS] [SERVICE...] [flags]
Flags:
-c, --context string Name of the cluster context to deploy to (default is the current context)
-f, --file strings One or more Compose files to deploy services from. (default compose.yaml)
-h, --help help for deploy
-n, --no-build Do not build images before deploying services. (default false)
-p, --profile strings One or more Compose profiles to enable.
--recreate Recreate containers even if their configuration and image haven't changed.
-y, --yes Auto-confirm deployment plan. Should be explicitly set when running non-interactively,
e.g., in CI/CD pipelines. [$UNCLOUD_AUTO_CONFIRM]
Global Flags:
--connect string Connect to a remote cluster machine without using the Uncloud configuration file. [$UNCLOUD_CONNECT]
Format: [ssh://]user@host[:port] or tcp://host:port
--uncloud-config string Path to the Uncloud configuration file. [$UNCLOUD_CONFIG] (default "~/.config/uncloud/config.yaml")For Deployment:
- added env var PORT=8080 to be aligned with the default expectation
- Not added env var DB_PATH, default is data/app.sqlite
- Update: added DB_PATH=/data/app.sqlite, since the default path might be wrong, dependning on the workdir configuration in the final docker image!
- added "Disk" in ui, basically the same as a volume
- redeployed
=> worked flawlessly => builds with nixpacks like railway and uses dockerfile under the hood
AFAIK:
- you can create templates
- these templates can be synced with another git repo, different to the one you deploying your app from (?!? - why tf)
- some issues with the initial deployment, the automatic detection got some things wrong as i gave the git repo to fly.io
- Good: volume was created and mounted correctly
- Question: Does scaling the volume via fly.toml work?
- Bad: Cold Start time is really bad (initial request did fail due to coldstart!)
- Good: volume was created and mounted correctly
- Question: Does scaling the volume via railway.toml work? => no, volume management is completely manual, via dashboard
CREATE TABLE todos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
text TEXT NOT NULL,
completed INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TRIGGER todos_updated_at
AFTER UPDATE ON todos
BEGIN
UPDATE todos SET updated_at = datetime('now') WHERE id = NEW.id;
END;- ✅ Add new todos
- ✅ Mark todos as complete/incomplete
- ✅ Edit todo text (double-click)
- ✅ Delete todos
- ✅ Persistent storage
- Bun.serve for HTTP handling
- SQLite database with prepared statements
- RESTful API with proper error handling
- Static file serving for HTML/CSS/TS
- TypeScript with DOM manipulation
- Fetch API for backend communication
- Simple, responsive UI
- Volume-mounted persistent storage
- Health check monitoring
- Automatic restarts on failure
- Environment-based configuration
- strict Infrastructure as Code, as far as possible
This is a template/scaffolding project. Feel free to:
- Fork and modify for your needs
- Add features (authentication, pagination, etc.)
- Improve the UI/UX
- Add tests
MIT