A self-hosted Laravel-based accounting and invoicing system for freelancers.
- Invoice & Quote Management: Create professional invoices and quotes with PDF generation
- Monthly Accounting: Automated bank statement parsing and expense tracking
- Financial Reporting: Monthly/yearly reports with visualizations
- AI-Powered Insights: Spending analysis and recommendations via multiple AI providers
- Paperless Integration: Automatic document archiving
- Docker & Docker Compose
- PostgreSQL 16+ (bundled for local dev, external for production)
- PHP 8.2+ (bundled in Docker image)
For local development using Laravel Sail with a bundled PostgreSQL database:
# Clone the repository
git clone <repository-url>
cd freelance-finance-hub
# Copy environment file
cp .env.example .env
# Install PHP dependencies (using Docker)
docker run --rm \
-u "$(id -u):$(id -g)" \
-v "$(pwd):/var/www/html" \
-w /var/www/html \
laravelsail/php82-composer:latest \
composer install --ignore-platform-reqs
# Start the development environment with local PostgreSQL
./vendor/bin/sail up -d
# Generate application key
./vendor/bin/sail artisan key:generate
# Run database migrations
./vendor/bin/sail artisan migrate
# Seed default data
./vendor/bin/sail artisan db:seed
# Install and build frontend assets
./vendor/bin/sail npm install
./vendor/bin/sail npm run devVisit http://localhost:8080 to access the application.
Default login credentials:
- Email:
admin@example.com - Password:
password
⚠️ Change these credentials immediately after first login!
For Dokploy deployment with a shared PostgreSQL instance:
Required variables:
| Variable | Description | Example |
|---|---|---|
APP_KEY |
Application encryption key | base64:xxxxx |
APP_URL |
Your production domain | https://finance.example.com |
APP_PASSWORD |
Admin authentication password | your-secure-password |
DATABASE_URL |
PostgreSQL connection string | postgresql://user:pass@host:5432/dbname |
Optional variables (with defaults):
| Variable | Default | Description |
|---|---|---|
APP_ENV |
production |
Environment mode |
APP_DEBUG |
false |
Debug mode (keep false in production) |
APP_LOCALE |
de |
Application locale |
LOG_LEVEL |
error |
Logging verbosity |
DB_SSLMODE |
prefer |
PostgreSQL SSL mode |
Before deploying, generate an application key:
# Option 1: Using PHP locally
php artisan key:generate --show
# Option 2: Using Docker
docker run --rm -it \
-v "$(pwd):/var/www/html" \
-w /var/www/html \
php:8.2-cli \
php artisan key:generate --showCopy the output (starts with base64:) to your APP_KEY environment variable.
Use docker-compose.production.yml for a clean production configuration:
docker compose -f docker-compose.production.yml up -dOr deploy directly via Dokploy's Git integration.
Migrations run automatically on container start. To run manually:
docker compose exec app php artisan migrate --forceFor standalone Docker deployment without Dokploy:
# Build the image
docker build -t freelance-finance .
# Run with external PostgreSQL
docker run -d \
-p 8080:80 \
-e APP_KEY="base64:your-generated-key" \
-e APP_URL="http://localhost:8080" \
-e APP_PASSWORD="your-password" \
-e DATABASE_URL="postgresql://user:pass@postgres-host:5432/freelance_finance" \
-v finance_storage:/app/storage/app \
-v finance_logs:/app/storage/logs \
--name freelance-finance \
freelance-finance| Variable | Required | Default | Description |
|---|---|---|---|
APP_NAME |
No | Freelance Finance Hub |
Application display name |
APP_ENV |
No | production |
local, staging, or production |
APP_KEY |
Yes | - | Encryption key (generate with artisan key:generate) |
APP_DEBUG |
No | false |
Enable debug mode |
APP_URL |
Yes | - | Full application URL |
APP_PASSWORD |
Yes | - | Admin authentication password |
APP_LOCALE |
No | de |
Default locale (de, en) |
You can configure the database connection in two ways:
Use a single DATABASE_URL environment variable:
DATABASE_URL=postgresql://username:password@hostname:5432/database_name?sslmode=requireURL Format:
postgresql://[user]:[password]@[host]:[port]/[database]?[params]
SSL Mode Options:
disable- No SSLallow- Try non-SSL, then SSLprefer- Try SSL, then non-SSL (default)require- Require SSLverify-ca- Require SSL + verify CAverify-full- Require SSL + verify CA + hostname
DB_CONNECTION=pgsql
DB_HOST=localhost
DB_PORT=5432
DB_DATABASE=freelance_finance
DB_USERNAME=your_username
DB_PASSWORD=your_password
DB_SCHEMA=public
DB_PREFIX=
DB_SSLMODE=prefer| Variable | Default | Description |
|---|---|---|
DB_SCHEMA |
public |
PostgreSQL schema name |
DB_PREFIX |
`` | Table name prefix (useful for shared databases) |
DB_SSLMODE |
prefer |
SSL connection mode |
DB_SSLCERT |
- | Path to client SSL certificate |
DB_SSLKEY |
- | Path to client SSL key |
DB_SSLROOTCERT |
- | Path to root CA certificate |
The application supports multiple AI providers via Prism PHP. Configure providers in Settings → Integrations or via environment variables.
OLLAMA_URL=http://localhost:11434
# For Docker: use host.docker.internal or your server IP
OLLAMA_URL=http://host.docker.internal:11434Recommended models:
- Text:
gpt-oss:20b(with thinking mode) - Vision:
qwen2.5vl:3b(for receipt extraction)
# OpenAI
OPENAI_API_KEY=sk-xxxxx
# Anthropic (Claude)
ANTHROPIC_API_KEY=sk-ant-xxxxx
# OpenRouter (access to 100+ models)
OPENROUTER_API_KEY=sk-or-xxxxx
# Other providers
MISTRAL_API_KEY=xxxxx
GROQ_API_KEY=xxxxx
XAI_API_KEY=xxxxx
GEMINI_API_KEY=xxxxx
DEEPSEEK_API_KEY=xxxxxConnect to your Paperless-ngx instance for automatic document archiving:
PAPERLESS_URL=http://your-paperless-server:8000/
PAPERLESS_API_TOKEN=your_api_token_here
PAPERLESS_STORAGE_PATH=SelbstständigkeitConfigure via UI:
- Navigate to Settings → Paperless Integration
- Enter your Paperless server URL and API token
- Select your storage path from the dropdown
- Save settings
What the storage path does:
- Filters all document searches to your selected path
- Uploads all documents (invoices, quotes) to this path
- Keeps business documents organized and separated
- Initial Setup: Configure company details in Settings → Company
- Customers: Add customer information for invoicing
- Invoices/Quotes: Create and manage documents with PDF export
- Monthly Accounting: Upload bank statements for automated expense tracking
- AI Analysis: Get spending insights and recommendations
- Reports: Generate financial reports and tax summaries
This application handles sensitive financial data. Follow these guidelines for secure deployment.
Before deploying publicly, ensure these settings are configured:
# Required in production .env
APP_ENV=production
APP_DEBUG=false
APP_URL=https://your-domain.com # Must use HTTPS
# Session security
SESSION_SECURE_COOKIE=true
SESSION_ENCRYPT=true
# Database security
DB_SSLMODE=require # If your database supports SSLHTTPS is required for production deployments handling financial data.
For Dokploy/reverse proxy setups, ensure:
- SSL termination at the proxy level
APP_URLuseshttps://SESSION_SECURE_COOKIE=trueis set
The Docker image includes nginx with security headers pre-configured:
| Header | Value | Purpose |
|---|---|---|
| X-Frame-Options | SAMEORIGIN | Prevents clickjacking |
| X-Content-Type-Options | nosniff | Prevents MIME sniffing |
| X-XSS-Protection | 1; mode=block | Legacy XSS protection |
| Content-Security-Policy | (configured) | Prevents XSS/injection |
| Referrer-Policy | strict-origin-when-cross-origin | Controls referrer info |
| Permissions-Policy | (restricted) | Disables unused APIs |
For production with HTTPS, use the enhanced nginx config with HSTS:
# Option 1: Copy production config
cp docker/nginx.production.conf docker/nginx.conf
# Option 2: Mount as volume in docker-compose.yml
volumes:
- ./docker/nginx.production.conf:/etc/nginx/http.d/default.conf:roThe production config (docker/nginx.production.conf) includes:
- HSTS header - Forces HTTPS for 1 year
- Additional CSP directives -
base-uri,form-action - Blocks sensitive files -
.env,.log,.sql,.bak - Hides PHP version - Removes
X-Powered-Byheader
The database seeder creates a default admin account:
- Email:
admin@example.com - Password:
password
You MUST change these credentials immediately after first login.
For production, consider:
- Changing the password via the UI immediately
- Or modifying
database/seeders/UserSeeder.phpbefore deployment - Or setting up users manually via
php artisan tinker
Login attempts are rate-limited to 5 attempts per email/IP combination. After 5 failed attempts, users must wait before trying again.
Sessions are stored in the database by default, providing:
- Centralized session management
- Easy session invalidation
- No file permission issues in containers
For additional security, enable session encryption:
SESSION_ENCRYPT=trueAPI tokens (Paperless, AI providers) are:
- Stored encrypted in the database (via Settings)
- Masked in the UI (
type="password") - Never logged or exposed in error messages
See TODO.md for a complete list of security improvements planned:
- Password reset functionality (not yet implemented)
- Two-factor authentication (not yet implemented)
- Email verification enforcement (not yet implemented)
If you discover a security vulnerability, please report it privately rather than opening a public issue.
# Run all tests
./vendor/bin/sail artisan test
# Run specific test file
./vendor/bin/sail artisan test --filter=DatabaseConfigTest
# Run with coverage
./vendor/bin/sail artisan test --coverage# Fix code style (Laravel Pint)
./vendor/bin/sail pint
# Static analysis (PHPStan)
./vendor/bin/sail composer phpstan# Development with bundled PostgreSQL
docker compose --profile local up -d
# Production with external PostgreSQL
docker compose up -d
# Or use the production-specific file
docker compose -f docker-compose.production.yml up -dError: "Connection refused"
- Check
DB_HOSTis accessible from the container - For Dokploy, ensure the database is on the same network
- Verify
DATABASE_URLformat is correct
Error: "SSL required"
- Set
DB_SSLMODE=requireor add?sslmode=requiretoDATABASE_URL
Check logs:
docker compose logs appCommon issues:
- Missing
APP_KEY: Generate withphp artisan key:generate --show - Database not ready: Container waits for PostgreSQL automatically
- Permission issues: Storage directories are created on startup
- Verify Ollama is running:
curl http://localhost:11434/api/tags - Check API keys are set correctly in environment
- For Docker, use
host.docker.internalinstead oflocalhost
Personal Use License
Copyright (c) 2025
This project is licensed for personal use only.
Permissions (for original author only):
- ✅ Commercial use
- ✅ Modification
- ✅ Distribution
- ✅ Private use
Restrictions (for all others):
- ❌ Commercial use
- ❌ Modification
- ❌ Distribution
- ❌ Public use
This software is provided "as is" without warranty. Only the original author retains commercial usage rights.
Note: This is a personal project. For commercial licensing inquiries, please contact the original author.