Companion repo for A Pattern for Local Dev: Runtime on the Host, Services in Containers.
A Rails 8 API backed by PostgreSQL, with the same development environment declared three ways using Flox, Nix, and Guix — declared, graph-backed technologies that define the full runtime surface: language, native libraries, build toolchain, and CLI tools.
The project runtime runs directly on the host, inside a declared environment. Backing services (PostgreSQL) run in containers. Engineers don't work inside containers; they connect to services using host, port, and credential settings defined in the environment.
This project doesn't just need Ruby. It also needs PostgreSQL client libraries, libyaml, a C compiler, make, pkg-config, curl, CA certificates, time zone data, and a few developer utilities. Flox, Nix, and Guix declare all of these as part of the project environment and resolve them deterministically via a package graph.
| File | Tool | What it declares |
|---|---|---|
.flox/env/manifest.toml |
Flox | Full runtime: Ruby, native libs, toolchain, env vars, aliases, services |
flake.nix |
Nix | Full runtime: same packages, same shell hooks |
manifest.scm + setup-env.sh |
Guix | Full runtime: same packages, shell init sourced separately |
All three provide the same developer experience once activated: identical aliases (dbup, dev, tests, rs, rc, etc.), identical environment variables, and identical gem paths.
- Flox installed
- Docker Engine (Linux) or Docker Desktop (macOS)
git clone https://github.com/flox/traveling-ruby && cd traveling-ruby
flox activate
dbup # start PostgreSQL in Docker
bundle install # install gems
bundle exec rails db:setup # create and seed the database
dev # start the Rails servercd traveling-ruby
nix develop
dbup
bundle install
bundle exec rails db:setup
devcd traveling-ruby
guix shell -m manifest.scm
source setup-env.sh
dbup
bundle install
bundle exec rails db:setup
devcurl http://localhost:3000/health
# => {"status":"ok","database":"connected"}
curl http://localhost:3000/items
# => [{"id":1,"name":"Example Item","description":"Created by db:seed ..."}]
curl -X POST http://localhost:3000/items \
-H "Content-Type: application/json" \
-d '{"item": {"name": "Hello", "description": "From a declared environment"}}'These aliases are available in all three environments:
| Command | What it does |
|---|---|
dbup |
Start PostgreSQL container |
dbdown |
Stop PostgreSQL container (data preserved) |
dbreset |
Destroy and recreate database from scratch |
dev |
Start Rails development server |
rs |
Start Rails server (binds to 0.0.0.0) |
rc |
Open Rails console |
tests |
Run the test suite |
build-image |
Build the production Docker image |
Each environment stores compiled gems in a separate cache directory to avoid native extension conflicts across different Ruby builds:
| Environment | Cache directory |
|---|---|
| Flox | $FLOX_ENV_CACHE/bundler/ (managed by Flox) |
| Nix | ~/.cache/traveling-rails-poc-nix/bundler/ |
| Guix | ~/.cache/traveling-rails-poc-guix/bundler/ |
Run bundle install once per environment.
The GitHub Actions workflow installs Flox and runs tests inside flox activate. CI uses the same declared runtime as local development — no separate Ruby version matrix or system dependency list to maintain.
The Dockerfile mirrors the runtime declared in the environment manifests using equivalent Debian packages. The mapping is explicit and documented in the Dockerfile header.
build-image
docker run -p 3000:3000 \
-e DATABASE_HOST=host.docker.internal \
-e DATABASE_USER=postgres \
-e DATABASE_PASSWORD=postgres \
-e SECRET_KEY_BASE=$(bundle exec rails secret) \
traveling-rails-poc.
├── .flox/ # Flox environment
│ └── env/manifest.toml # packages, env vars, aliases, services
├── flake.nix # Nix equivalent (nix develop)
├── manifest.scm # Guix equivalent (guix shell -m manifest.scm)
├── setup-env.sh # shell init for the Guix environment
├── .github/workflows/ci.yml # CI via Flox
├── docker-compose.yml # PostgreSQL for local dev
├── Dockerfile # Production image
├── scripts/ # Developer workflow commands
│ ├── dev # start dev server
│ ├── db-up # start PostgreSQL
│ ├── db-down # stop PostgreSQL
│ ├── db-reset # reset database
│ ├── test # run tests
│ └── build-image # build production image
├── app/ # Rails application
├── config/ # Rails configuration
├── db/ # Migrations and seeds
└── test/ # Test suite