Forecast-first budgeting for household cash flow planning. The app is built as a Laravel API with a lightweight browser UI on top. Business logic should live in the API so other clients, such as a future mobile app, can use the same behavior.
Income sourcesare scheduled inflows. They can be one-time or recurring.Billsare scheduled outflows tied to optional payees and payment portal links.Expensesare scheduled outflows for general spending such as groceries, fuel, or other recurring household costs.Bucketsare savings goals for trips, purchases, reserves, or other planned set-asides.Forecast occurrencesare the generated calendar/paycheck instances from income, bill, expense, and bucket schedules.Allocationsreserve money from paycheck occurrences toward bill, expense, or bucket occurrences. The app does not auto-save suggested allocations; suggestions must be reviewed and saved.Paychecksshow current and upcoming income occurrences, allocated items, and relevant underfunded bills, expenses, or buckets.Setupis where income sources, bills, expenses, buckets, and admin-managed users are maintained.
Normal access uses Google login, but only users already added to the household user list can sign in. This is an allowlist, not SAML or domain-wide Google Workspace auth.
There is also a maintenance/local login path for fallback admin access. Local login should normally be enabled only for admin users.
Install dependencies:
composer install
npm installCreate local environment and database:
cp .env.example .env
php artisan key:generate
touch database/database.sqlite
php artisan migrateRun the app:
php artisan serve
npm run devUseful local commands:
php artisan test
npm run build
php artisan optimize:clear
php artisan route:list --path=apiImportant environment values:
APP_KEY=base64:...
APP_URL=https://budget.example.com
FRONTEND_URL="${APP_URL}"
DB_CONNECTION=sqlite
DB_DATABASE=/absolute/path/to/database.sqlite
SESSION_DRIVER=database
SESSION_DOMAIN=null
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_REDIRECT_URI="${APP_URL}/auth/google/callback"For Docker deployment, keep LOG_CHANNEL=stderr so Laravel exceptions appear
in container logs.
Create or update the fallback local admin:
php artisan household:ensure-local-adminNon-interactive form:
php artisan household:ensure-local-admin \
--name="Admin" \
--email="admin@example.com" \
--password="long-maintenance-password"Inside Docker:
docker compose exec app php artisan household:ensure-local-adminGenerate an app key for deployment:
docker compose run --rm app php artisan key:generate --showPut the generated base64:... value into the deployment .env or
.env.docker file as APP_KEY.
The app image is PHP-FPM only. Compose runs nginx separately and shares the built public assets through a named Docker volume.
Local Docker build/run:
cp .env.docker.example .env.docker
docker compose build
docker compose up -dPull-based deployment files live in docker/deploy/. That bundle can be copied
to a server or split into a small deployment repo.
Current published image:
docker.io/somethingamusing/budget:260616-02
On the Docker host, make sure the app service receives Laravel environment
values. Compose reads .env for variable substitution, but it does not pass
that file into containers unless the service includes env_file.
Example:
services:
app:
env_file:
- .envDeploy/update:
docker compose pull app
docker compose up -d --force-recreate app nginxCheck logs:
docker compose logs app
docker compose exec app sh -lc 'tail -n 120 storage/logs/laravel.log'Persistent SQLite data lives in the app_storage Docker volume at:
/var/www/html/storage/app/database.sqlite
Keep budgeting behavior in Laravel controllers/services, not in the browser UI. The frontend can manage display state, modal state, and form rendering, but logic such as allocation options, funding state, schedule generation, and access control should remain API-owned.
Before pushing an image or deploying:
php artisan test
npm run build
docker compose --env-file docker/deploy/.env.docker.example -f docker/deploy/compose.yaml configFor production image publishing:
docker compose build app
docker tag budget-app:local docker.io/somethingamusing/budget:<tag>
docker push docker.io/somethingamusing/budget:<tag>