A budgeting app for building month-by-month spending plans, tracking real activity, and reusing the recurring parts of a household budget.
- Overview
- Quick Start (Docker)
- Screenshots
- Features
- Tech Stack
- Getting Started
- Authentication
- Self-Hosted HTTPS
- Published Docker Images
- Updating a Self-Hosted Install
- Demo and Sample Data
- User Documentation
- Open Source Readiness
- Development Commands
- Troubleshooting
- Docker Files
Expense Tracker is built for people who budget by month and want one place to plan income, fixed bills, variable spending, debt payments, and carry-over decisions.
It also includes a manual accounts area for tracking balances in checking, savings, brokerage, retirement, and debt accounts without relying on live bank syncing.
Recurring transactions and month entries can optionally link to accounts, so account context carries through month workflows instead of living only in the balances area.
Hosted product overview and screenshots: https://financetracking.app/
Hosted user documentation for current app behavior: https://financetracking.app/docs.html
With it, a user can:
- restart from an overview dashboard that highlights the current month, quick actions, recurring status, and account context
- build a fresh month or start from a previous month instead of recreating the same structure every time
- review the same budget in a timeline, calendar, or editable list depending on how they like to think about money
- add manual adjustments and one-off entries once the recurring structure is in place
- reuse recurring items so routine planning takes less manual work through dedicated recurring transactions
- preview and restore versioned JSON backups, including optional encrypted exports and reference sample backup files
- estimate credit-card payments based on the cash left in the month rather than guessing in isolation
- record manual account balance snapshots and review a simple net worth trend over time
- keep in-app help available so the intended workflow is documented inside the product
This README focuses on installing, configuring, seeding, and operating the app. User-facing walkthroughs of the current product flow now live in the hosted documentation above.
For most people, the easiest way to try the app is the Docker setup below because it avoids local Ruby, PostgreSQL, and system package setup.
If the goal is to get the app running as quickly as possible, use Docker:
- Clone the repository and move into it
git clone <repo-url>cd expense_tracker
- Install Docker Desktop
- Optional: create a local env file for port, admin, or seed overrides
cp .env.example .env
- Start the app
docker compose up --build
- Open the app
- Optional: load seed data in another terminal
- users only:
docker compose exec web bin/rails db:seed - users with transactions:
docker compose exec web env SEED_MODE=users_with_transactions bin/rails db:seed - all dev test users:
docker compose exec web env SEED_PROFILE=all_test_users SEED_MODE=users_with_transactions bin/rails db:seed
- users only:
This quick-start Docker setup builds from a local git checkout of the repository. For production self-hosting from a prebuilt release image, see Published Docker Images.
If you set ADMIN_USER_EMAIL and ADMIN_USER_PASSWORD in .env before starting Docker, the app will create or update the admin account automatically during startup.
If 4287 is already in use, set APP_PORT before starting Docker, for example APP_PORT=4317 docker compose up --build.
After startup, admins can sign in through /admin/sign_in if ADMIN_USER_EMAIL and ADMIN_USER_PASSWORD were configured. Regular users can create their own accounts at /users/sign_up. After seeding, you can also sign in with the demo account described in the Sample User section. Use SEED_MODE=users_with_transactions if you also want the sample month and seeded transaction history, or SEED_PROFILE=all_test_users when you want multiple QA-focused seed personas.
Current screenshots reflect the latest overview, month-budget workflow, money-flow review, account tracking, account activity, and backup workflow.
- Start from an overview dashboard that surfaces the current month, attention items, recurring progress, account summaries, and quick actions
- Track manual balances for savings, investment, cash, and debt accounts without coupling budgeting to bank-sync reliability
- Link recurring transactions and entries to accounts while still allowing a manual account label when needed
- Show linked account context directly in month review views and account activity
- Reuse recurring items like paychecks, subscriptions, monthly bills, payment plans, and credit cards so routine planning takes less effort
- Save supported wizard-created entries directly as recurring transactions when you want a one-off action to become reusable later
- Recalculate card payment estimates from available leftover cash so payoff planning stays aligned with the rest of the month
- Plan each month in one place so income, bills, subscriptions, debt payments, and discretionary spending stay visible together
- Start a new month quickly by cloning an existing one, which saves time when your budget structure stays mostly the same
- Keep workflow guidance available inside the app through a dedicated Help area
- Avoid accidental duplicate generation on older completed months with safeguards that hide actions you likely no longer need
- See your budget in multiple views so you can review the same data as a timeline, a calendar, a breakdown, or a detailed entry list
- Add transactions the way that fits your workflow, whether that means entering them manually, using the guided wizard, or launching the wizard from month views
- Upload past transactions to get a month populated faster instead of rebuilding everything by hand
- Filter entries by the reasons and categories that actually appear in your month, making it easier to focus on specific spending patterns
- Toggle between grouped timeline sections and a full month list without leaving the same review surface
- Export and restore recurring transactions, months, and account data through versioned JSON backups with optional password encryption, including manual entries linked back to recurring items
- Preview imports before restoring anything, and use a sample backup file to inspect the expected structure
- Keep each person’s budget private behind sign-in so one account only sees its own months and entries
For developers and contributors, the app is built with:
- Ruby 4.0.5
- Rails 8.1.2
- PostgreSQL
- Devise
- Turbo + Stimulus
- Tailwind CSS
- Chart.js (loaded from CDN for line, bar, doughnut, and other standard charts)
- Apache ECharts (loaded from CDN for the monthly flow graph)
- RSpec + FactoryBot
For most users, Docker is the recommended way to run the app.
Use Docker if you want the quickest path to opening the app without manually setting up Ruby, PostgreSQL, or system dependencies.
Use the local setup only if you plan to develop on the project or prefer managing those dependencies yourself.
This is the recommended setup for most users.
The repository includes a Docker-based environment that starts the Rails app and PostgreSQL together.
For local evaluation and development, run Docker Compose from a local clone and rebuild from source. For production self-hosting, you can either build the image from your checkout or set EXPENSE_TRACKER_IMAGE to a published release image from GHCR or Docker Hub.
- Docker Desktop, or Docker Engine + Docker Compose
- Clone the repository and move into it
git clone <repo-url>cd expense_tracker
- Optional: copy the example environment file and adjust any values you want to override
cp .env.example .env
- Build and start the containers
docker compose up --build
- Open the app
Start the full Compose stack from this directory. Do not start the web container by itself with docker run or from a GUI action that skips the db service, because the Rails container expects to reach PostgreSQL at the Compose hostname db.
For access from the same machine, use http://localhost:4287.
For access from another device on your network, use http://YOUR_COMPUTER_IP:4287. The Docker setup enables non-localhost hosts for development, but your OS firewall and router still need to allow inbound connections to that port.
The Docker setup publishes the app on host port 4287 by default to avoid the more commonly used 3000.
To override it, set APP_PORT in your shell or a local .env file before starting Docker.
For convenience, copy .env.example to .env and edit the values you want to override.
Because this flow builds from your local checkout, later source-based updates are done from this same directory with:
git pulldocker compose up -d --build
Examples:
APP_PORT=4317 docker compose up --build.envfile entry:APP_PORT=4317
Services included:
web— Rails app running viabin/devdb— PostgreSQL 16 on the internal Compose network
The PostgreSQL container is not published to a host port by default. The Rails app connects to it over the Compose network as db, which avoids conflicts on machines that already use port 5432 for another local PostgreSQL instance.
The container entrypoint automatically runs bin/rails db:prepare when the app starts.
If ADMIN_USER_EMAIL and ADMIN_USER_PASSWORD are present in the container environment, the entrypoint also runs bin/rails admin:bootstrap so the admin account exists from the initial install.
In another terminal:
- users only:
docker compose exec web bin/rails db:seed - users with transactions:
docker compose exec web env SEED_MODE=users_with_transactions bin/rails db:seed - all dev test users:
docker compose exec web env SEED_PROFILE=all_test_users SEED_MODE=users_with_transactions bin/rails db:seed
The default command creates or refreshes a demo account with reusable recurring transactions, linked manual accounts across the supported account kinds, and balance snapshots. Use SEED_MODE=users_with_transactions to also create the sample month and seeded transaction history. Use SEED_PROFILE=all_test_users to add a broader set of targeted dev personas for QA and UI review.
If you prefer storing these overrides in .env before running Docker, set:
ADMIN_USER_EMAIL=admin@example.comADMIN_USER_PASSWORD=strong-passwordSEED_MODE=users_with_transactionswhen you want full demo dataSEED_PROFILE=all_test_userswhen you want multiple targeted test users
Then:
- run
docker compose up --buildto install and bootstrap the admin user automatically - run
docker compose exec web bin/rails db:seedonly if you also want the demo user and sample budgeting data
From there, the admin can sign in through /admin/sign_in, and end users can create their own accounts through /users/sign_up.
Due recurring-generated entries are automatically marked as done by setting their status to paid and copying the planned amount into the actual amount when needed.
This works in two ways:
- month pages run a small sync when opened, so self-hosted installs still update during normal use
- production also schedules a daily Solid Queue job for unattended auto-completion
For self-hosted production:
- single-server installs can keep using the existing
SOLID_QUEUE_IN_PUMA=truesetup so jobs run inside Puma - multi-server installs should move job processing to a dedicated
bin/jobsprocess or job host
The recurring schedule lives in config/recurring.yml.
docker compose down
To also remove the database volume:
docker compose down -v
This setup is mainly for developers and contributors.
- Ruby 4.0.5
- Bundler
- PostgreSQL
- libpq development headers
- libvips
Typical macOS setup with Homebrew:
brew install postgresql libpq vips
Make sure PostgreSQL is running before starting the app.
- Create a local env file
cp .env.example .env
- Install gems
bundle install
- Prepare the database
bin/setup --skip-server
- Optional: load seed data
- users only:
bin/rails db:seed - users with transactions:
SEED_MODE=users_with_transactions bin/rails db:seed
- users only:
- Start the development server
bin/dev
Open http://localhost:3000. Admins sign in through /admin/sign_in, and regular users can register through /users/sign_up before signing in to start creating budget months.
The Accounts & Net Worth area is available from the signed-in sidebar if you want to track manual balances alongside the monthly budgeting workflow.
bin/setup runs bin/rails db:prepare and then bin/rails admin:bootstrap. If ADMIN_USER_EMAIL and ADMIN_USER_PASSWORD are set in .env, the admin account is created or updated automatically as part of local install.
dotenv-rails is enabled in development, so values in .env are loaded automatically when you run Rails commands locally.
Common local .env uses:
- override
PORTforbin/dev - override
APP_PORTfor Docker - set
ADMIN_USER_EMAILandADMIN_USER_PASSWORDwhen you want install-time admin bootstrap - set
SEED_MODE,SEED_USER_EMAIL, andSEED_USER_PASSWORDbefore runningbin/rails db:seed - set
DATABASE_URLif you want to connect to PostgreSQL over TCP instead of the default local socket setup
The app requires sign-in so each account only sees its own months, entries, imports, and recurring transactions.
There is also a separate admin authentication surface for user-access management. The admin console is intentionally limited to identity metadata, access-state changes, and admin audit logs. It does not provide routes for viewing budget months, entries, recurring transactions, or account balances.
The intended install flow is:
- set
ADMIN_USER_EMAILandADMIN_USER_PASSWORD - run
bin/setup --skip-serverlocally ordocker compose up --buildin Docker - sign in to
/admin/sign_in - let end users register through
/users/sign_up
You can:
- create a new regular user account from
/users/sign_up - sign in as an admin from
/admin/sign_in - sign in with your own account
- use the seeded demo account after running
bin/rails db:seed
The default seed creates the demo user with recurring transactions and account data. Use SEED_MODE=users_with_transactions if you also want seeded month data and transaction history.
Admin provisioning is normally handled during install by bin/setup, the Docker entrypoint, or bin/rails admin:bootstrap when ADMIN_USER_EMAIL and ADMIN_USER_PASSWORD are set.
Examples:
ADMIN_USER_EMAIL=admin@example.com ADMIN_USER_PASSWORD=password123! bin/setup --skip-serverADMIN_USER_EMAIL=admin@example.com ADMIN_USER_PASSWORD=password123! bin/rails admin:bootstrapdocker compose up --buildwith those same values present in.env
If only one of those admin env vars is set, admin bootstrap fails fast so you do not end up with a half-configured admin setup.
Optional Cloudflare Turnstile protection is available for the public authentication entry points:
- user sign in
- user sign up
- password reset request
- admin sign in
To enable it, set both TURNSTILE_SITE_KEY and TURNSTILE_SECRET_KEY before booting the app. If either value is missing, the widget stays disabled and authentication behaves normally.
The Turnstile widget is rendered explicitly on the shared authentication layout so it can recover cleanly across Turbo visits and cached page restores.
You can still create an admin manually from the Rails console if that fits your deployment workflow better:
bin/rails consoleAdminUser.create!(email: "admin@example.com", password: "password123!", password_confirmation: "password123!")
For stronger hardening in production, run the admin surface with a restricted PostgreSQL role that can only read users, admin_users, and admin_audit_logs, plus update users.access_state.
For a public self-hosted deployment, run the Rails app behind a reverse proxy that terminates TLS instead of exposing the app container directly.
This repository includes a Caddy-based production example:
- Copy the production environment template.
cp .env.production.example .env.production
- Set a real domain name in
APP_HOST. - Set strong values for
POSTGRES_PASSWORD,SECRET_KEY_BASE, andRAILS_MASTER_KEY. - Point your DNS record at the server.
- Start the production stack.
docker compose --env-file .env.production -f docker-compose.production.yml up -d --build
The command above builds the Rails image from your local checkout. To use a published release image instead, set EXPENSE_TRACKER_IMAGE in .env.production, pull the web image, and start the stack with --no-build.
This setup publishes only Caddy on ports 80 and 443. Rails stays private on the internal Docker network and receives forwarded HTTPS traffic from Caddy.
Included files for this flow:
docker-compose.production.ymldeploy/Caddyfile.env.production.example
Rails production is configured to expect an SSL-terminating proxy by default. If you run production over plain HTTP (e.g. LAN without HTTPS), set ASSUME_SSL=false and FORCE_SSL=false in your .env.production file so cookies and redirects work correctly.
For a private LAN install where you do not want public certificates, use the dedicated Caddy config that issues certificates from Caddy's internal CA.
- Copy the production environment template.
cp .env.production.example .env.production
- Set
APP_HOSTto the hostname you will actually use on your LAN, such asbudget.lan. - Start the same production stack with the LAN HTTPS override.
docker compose --env-file .env.production -f docker-compose.production.yml -f docker-compose.lan-https.yml up -d --build
- Install Caddy's internal root CA on each client device that will access the app.
Files for this variant:
docker-compose.lan-https.ymldeploy/Caddyfile.internal
This is appropriate for private networks and self-hosted setups without public DNS. Browsers will warn about the site until the internal CA is trusted on the client device.
After the stack starts for the first time, Caddy creates its internal root CA inside the caddy_data volume. You need to export that root certificate and trust it on each client device that will open the app.
One simple way to copy it out is:
- Start the LAN HTTPS stack.
docker compose --env-file .env.production -f docker-compose.production.yml -f docker-compose.lan-https.yml up -d --build
- Copy the root certificate from the running Caddy container.
docker compose --env-file .env.production -f docker-compose.production.yml -f docker-compose.lan-https.yml cp caddy:/data/caddy/pki/authorities/local/root.crt ./caddy-local-root.crt
- Install
caddy-local-root.crtinto the trust store of each browser or device that should trust your LAN deployment.
Typical trust flow:
- macOS: open Keychain Access, import
caddy-local-root.crtinto theSystemorloginkeychain, then mark it asAlways Trust - iPhone/iPad: AirDrop or host the certificate file locally, install the profile, then enable full trust for the root certificate in Settings
- Windows: import the certificate into
Trusted Root Certification Authorities - Linux: import it into the system CA store or the browser-specific store, depending on your distro and browser
If you rebuild or replace the caddy_data volume, Caddy may generate a new internal CA and you would need to trust the new root certificate again.
Release images are published from the same workflow that creates GitHub Releases.
Published image providers:
- GitHub Container Registry:
ghcr.io/taimoorq/expense_tracker - Docker Hub:
docker.io/<dockerhub-username>/expense-tracker
Each provider receives the same tag set:
vX.Y.ZX.Y.Zlatestsha-<commit>
For repeatable production deploys, prefer a version tag such as ghcr.io/taimoorq/expense_tracker:v0.5.11 instead of latest.
To run the production Compose stack from a published image:
-
Set the app image in
.env.production.EXPENSE_TRACKER_IMAGE=ghcr.io/taimoorq/expense_tracker:v0.5.11
Or use the Docker Hub mirror:
EXPENSE_TRACKER_IMAGE=docker.io/<dockerhub-username>/expense-tracker:v0.5.11
-
Pull the image.
docker compose --env-file .env.production -f docker-compose.production.yml pull web
-
Start the stack without rebuilding locally.
docker compose --env-file .env.production -f docker-compose.production.yml up -d --no-build
If the GHCR package is not anonymously pullable, make the package public in GitHub's package settings or authenticate Docker to ghcr.io before pulling. If the Docker Hub repository is private, authenticate Docker to Docker Hub before pulling.
Replace <dockerhub-username> with the Docker Hub namespace configured in the DOCKERHUB_USERNAME repository secret.
Docker Hub publishing requires repository secrets named DOCKERHUB_USERNAME and DOCKERHUB_TOKEN.
Most self-hosted users should update by pulling the latest code and then restarting the app in a way that reruns db:prepare.
Before updating:
- back up your PostgreSQL database
- review
.env.exampleand compare it to your existing.envfor any new required settings - keep your current database volume or database server intact so user data is preserved
The app checks the latest GitHub Release for this repository and shows an Update Available button near the bottom of the sidebar when the published release tag is newer than the local app version in config/releases.yml. It also shows a main-page update banner with a link to these instructions so the notice stays visible even when the sidebar is collapsed. The check is cached for 15 minutes and silently disappears if GitHub cannot be reached.
Optional update-check settings:
GITHUB_UPDATE_CHECKS_ENABLED=falsedisables the GitHub release check.GITHUB_UPDATE_REPOSITORY=owner/repochecks a different GitHub repository.GITHUB_UPDATE_TOKEN=...adds a token for private repositories or higher GitHub API rate limits.GITHUB_UPDATE_README_URL=...changes where the sidebar update button links.
This app includes an in-product release feed for self-hosted users. Developers should update it whenever a deploy includes user-visible changes worth calling out.
For each new release:
- Add a new top entry to
config/releases.yml. - Use a new version string, release date, short title, one-sentence summary, and a few plain-language change bullets.
- Keep the newest release first so it becomes the current in-app version.
- Ship that file change in the same commit or release branch as the product changes it describes.
- After that change lands on
main, GitHub Actions automatically creates the matching git tag, publishes the GitHub Release if that version does not already exist yet, and publishes Docker images for that version.
How it works:
- the latest entry in
config/releases.ymlis treated as the current app release - signed-in users see a banner and Help badge until they mark that release as read
- old entries remain visible in Help as release history, so do not delete them during normal updates
.github/workflows/publish_release.ymlreads the top release entry on pushes tomain, publishesvX.Y.Z, and pushes Docker image tagsvX.Y.Z,X.Y.Z,latest, andsha-<commit>
If you are running the included Docker setup:
- Pull the latest code.
- Review
.env.examplefor any new environment variables and update your.envif needed. - Rebuild and restart the app:
docker compose up -d --build
- Check logs if needed:
docker compose logs -f web
What happens during startup:
- the container entrypoint runs
bin/rails db:prepare, so new migrations are applied automatically - it also runs
bin/rails admin:bootstrap, so admin credentials stay in sync withADMIN_USER_EMAILandADMIN_USER_PASSWORDif you changed them
What not to do during a normal update:
- do not run
docker compose down -vunless you intentionally want to delete the database volume - do not run
db:seedunless you explicitly want demo/sample data
If you are running the production stack from a published image, update EXPENSE_TRACKER_IMAGE to the new version tag, then run:
docker compose --env-file .env.production -f docker-compose.production.yml pull webdocker compose --env-file .env.production -f docker-compose.production.yml up -d --no-build
If you are running the app directly on a server without Docker:
- Pull the latest code.
- Review
.env.exampleand update your.envif needed. - Install any new gems:
bundle install
- Run setup again:
bin/setup --skip-server
- Restart the app process:
bin/dev
bin/setup --skip-server reruns db:prepare and admin:bootstrap, so schema updates and install-time admin configuration are applied as part of the update.
Regular user accounts, budget months, recurring transactions, snapshots, and admin audit logs are stored in the database and survive normal updates.
bin/rails db:seed is only for creating or refreshing the demo user and sample data. It is not required for routine self-hosted upgrades.
This project includes demo data for evaluation and sample files for testing imports.
Running bin/rails db:seed creates or updates a demo user you can sign in with.
By default this is a users-only seed with reusable recurring transactions, linked accounts, and manual balance history but no budget month history. To also load six past months of generated demo history, run SEED_MODE=users_with_transactions bin/rails db:seed.
If ADMIN_USER_EMAIL and ADMIN_USER_PASSWORD are present, the same seed run can also create or update an admin user, but the normal path is to bootstrap that admin during install before other users begin signing up.
Seeded credentials:
- Email:
demo@example.com - Password:
password123!
All seeded non-admin users created in the same run use the current SEED_USER_PASSWORD value. If you set SEED_USER_PASSWORD=my-secret, every seeded test user created by that run will use my-secret.
You can override these when seeding with:
SEED_PROFILE=demo,new_user,recurring_heavy,month_history_heavy,account_heavy,manual_adjustments, orall_test_usersSEED_MODE=usersorSEED_MODE=users_with_transactionsSEED_USER_EMAIL=your-email@example.comSEED_USER_PASSWORD=your-passwordADMIN_USER_EMAIL=admin@example.comADMIN_USER_PASSWORD=choose-a-strong-password
The seed script now supports multiple dev-focused personas:
demo: the balanced default user with recurring transactions, linked accounts, and optional month historynew_user: a near-empty account for onboarding and first-run testingrecurring_heavy: many recurring transactions with no month history, useful for recurring-library viewsmonth_history_heavy: a recurring-rich user with 12 months of history for timeline and month-review testingaccount_heavy: broader account coverage with extra snapshots and linked recurring/account behaviormanual_adjustments: realistic months with exceptions, skipped items, and manual entries linked back to recurring transactionsall_test_users: creates the full set above in one run for local QA
When SEED_PROFILE=all_test_users, the seeded user emails are:
- primary demo user: whatever
SEED_USER_EMAILis set to, ordemo@example.comby default new-user@example.comrecurring-heavy@example.commonth-history@example.comaccount-heavy@example.commanual-adjustments@example.com
Suggested dev command:
SEED_PROFILE=all_test_users SEED_MODE=users_with_transactions bin/rails db:seedSEED_PROFILE=all_test_users SEED_MODE=users_with_transactions SEED_USER_PASSWORD=password123! bin/rails db:seed
When SEED_MODE=users_with_transactions, the seed process also generates six past months of demo history from the seeded recurring transactions and accounts.
The demo data also:
- attaches all seeded records to the sample user
- creates starter recurring transactions for pay, subscriptions, bills, plans, and cards
- links those recurring transactions to seeded accounts where appropriate so account rollups and account activity views have representative data
- creates manual accounts across checking, savings, brokerage, retirement, cash, asset, credit-card, loan, and other-liability categories with balance snapshots
- creates generated month entries from those recurring transactions across the previous six months
- adds realistic manual spending, savings, and investing entries on top of the generated recurring entries
- keeps demo cashflow positive for the seeded months
- prints a summary of what was created or refreshed
Sample import files are available in public/samples/ and downloadable from the app:
monthly_transactions_template.csvsample_month_common_payments.csv
Expected transaction columns:
MonthDateSectionCategoryPayeePlanned AmountActual AmountAccountStatusNeed or WantNotes
Current user-facing documentation lives on the hosted site instead of in this README:
That guide covers:
- the overview dashboard and how to re-enter the workflow quickly
- Accounts & Net Worth, including linked-account behavior
- creating or cloning months
- adding entries manually or with the guided wizard
- recurring transactions and the current account-linkage model
- reviewing a month in Budget, Breakdown, Calendar, and Plan and Edit
- backup and restore behavior, including account-aware exports and restores
- how the hosted docs and in-app Help page complement each other
This section is for maintainers preparing the repository for public sharing.
This repo is already set up to avoid committing the usual local-only files, including:
config/*.key.env*log/*tmp/*storage/*.vscode/
Before publishing, review this checklist:
- Confirm secrets were never committed
config/master.keyis ignored and is not currently tracked.- If a real secret was ever committed in the past, rotate it before publishing.
- Keep deployment config as example-only
- config/deploy.yml now uses placeholder hosts and registry values.
- Review sample data and screenshots
- Seed/sample CSVs use generic names and demo content.
- Screenshots currently show demo data and a demo account identity.
- Regenerate credentials if needed
- config/credentials.yml.enc is safe to publish only if the matching key is never shared.
- If you are unsure what is inside, create fresh credentials before publishing.
- Check git history one more time before pushing
- Review for accidental secrets, exported data, or personal notes.
Recommended pre-publish commands:
git log -- config/master.keygit log -- log/test.loggit grep -n "@"git grep -n "password"git grep -n "/Users/"
These commands are mainly for local development, debugging, and contribution work.
- Lint Ruby:
bin/rubocop - Security scan:
bin/brakeman - Install git hooks:
bin/rails git_hooks:install
- Start app:
bin/dev - Setup app:
bin/setup --skip-server - Prepare DB:
bin/rails db:prepare - Bootstrap admin from env:
bin/rails admin:bootstrap - Seed users only:
bin/rails db:seed - Seed users with transactions:
SEED_MODE=users_with_transactions bin/rails db:seed - Run tests:
bundle exec rspec - Rails console:
bin/rails console - Autoload check:
bin/rails zeitwerk:check
- Start app:
docker compose up --build - Start app with env-driven admin bootstrap: set
ADMIN_USER_EMAILandADMIN_USER_PASSWORDin.env, then rundocker compose up --build - Seed users only:
docker compose exec web bin/rails db:seed - Seed users with transactions:
docker compose exec web env SEED_MODE=users_with_transactions bin/rails db:seed - Run tests:
docker compose exec web bundle exec rspec - Rails console:
docker compose exec web bin/rails console - Stop app:
docker compose down
This repository includes a tracked pre-commit hook in .githooks/pre-commit.
To enable it locally, run:
bin/rails git_hooks:install
After that, each commit runs bin/rubocop against staged Ruby files and blocks the commit if lint fails.
These notes are intended for contributors running the project locally.
Stop the process using it, or start Docker with a different APP_PORT value.
- Local: make sure PostgreSQL is running
- Docker: make sure the
dbcontainer is healthy
If the web container logs could not translate host name "db" to address, the app was almost certainly started outside the normal Compose network.
Use these checks:
docker compose psdocker compose logs db
The expected fix is to start both services together from the repository directory:
docker compose up --build
Do not start the Rails container by itself with docker run unless you also provide a reachable PostgreSQL host and override DATABASE_URL accordingly.
If Docker fails with Bind for 0.0.0.0:5432 failed: port is already allocated, another process on your machine is already using host port 5432.
The default Compose file no longer publishes PostgreSQL to the host, so pulling the latest code and restarting with docker compose up --build should avoid that conflict.
If the app starts but is not reachable from another device, check these in order:
- confirm it opens on the host machine at
http://localhost:4287 - find the host machine's LAN IP and try
http://YOUR_COMPUTER_IP:4287 - allow inbound connections for Docker or your terminal app in the OS firewall
- make sure you are on the same local network and not crossing a guest or isolated VLAN
If it works on localhost but not on the LAN IP, the problem is outside Rails and is usually firewall or network policy.
Docker Compose creates a default bridge network for each project. Docker may auto-allocate a subnet that conflicts with your host LAN (e.g. 192.168.x.x), which can break routing and prevent other containers from communicating over the host IP. The compose files use explicit subnets (172.28.1.0/24 for dev, 172.28.2.0/24 for production) to avoid this. If you still see conflicts, you can change the subnet in the networks section of docker-compose.yml to a range that does not overlap your LAN.
docker compose up --build
These files matter primarily for developers working on the project locally or preparing deployment-related changes.
Dockerfile— production-oriented imageDockerfile.dev— local development imagedocker-compose.yml— local multi-container setup
For local Docker development, use Dockerfile.dev and docker-compose.yml.