Invoice Ninja is a powerful open-source invoicing and billing platform designed for freelancers and businesses. It provides features for creating invoices, quotes, managing clients, tracking payments, time tracking, and expense management.
1. Prerequisites
2. Supported Operating Systems
This guide supports installation on:
3. Installation
Docker Installation (Recommended)
1. Create directory structure:
# Create base directory
sudo mkdir -p /opt/invoiceninja
cd /opt/invoiceninja
# Create required directories
mkdir -p docker/app/public docker/app/storage nginx/conf.d
2. Create docker-compose.yml:
version: '3.7'
services:
nginx:
image: nginx:alpine
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./docker/app/public:/var/www/app/public:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- app
networks:
- invoiceninja
app:
image: invoiceninja/invoiceninja:5
restart: always
env_file: .env
volumes:
- ./docker/app/public:/var/www/app/public
- ./docker/app/storage:/var/www/app/storage
depends_on:
- db
- redis
networks:
- invoiceninja
db:
image: mariadb:10.6
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
volumes:
- db_data:/var/lib/mysql
networks:
- invoiceninja
redis:
image: redis:alpine
restart: always
volumes:
- redis_data:/data
networks:
- invoiceninja
volumes:
db_data:
redis_data:
networks:
invoiceninja:
driver: bridge
3. Create environment file:
# Create .env file
cat > .env << 'EOF'
# Application
APP_ENV=production
APP_DEBUG=false
APP_URL=https://invoice.example.com
APP_KEY=base64:$(openssl rand -base64 32)
APP_CIPHER=AES-256-CBC
# Database
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=invoiceninja
DB_USERNAME=invoiceninja
DB_PASSWORD=$(openssl rand -base64 32)
# MySQL Root
MYSQL_ROOT_PASSWORD=$(openssl rand -base64 32)
MYSQL_DATABASE=invoiceninja
MYSQL_USER=invoiceninja
MYSQL_PASSWORD=${DB_PASSWORD}
# Cache and Queue
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis
REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379
# Mail
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=your-email@gmail.com
MAIL_PASSWORD=your-app-password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=noreply@invoice.example.com
MAIL_FROM_NAME="Invoice Ninja"
# Additional settings
REQUIRE_HTTPS=true
TRUSTED_PROXIES="*"
PDF_GENERATOR=snappdf
PHANTOMJS_PDF_GENERATION=false
EOF
4. Configure nginx:
# Create nginx configuration
cat > nginx/conf.d/invoiceninja.conf << 'EOF'
server {
listen 80;
server_name invoice.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name invoice.example.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
root /var/www/app/public;
index index.php;
client_max_body_size 50M;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass app:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_buffer_size 32k;
fastcgi_buffers 8 16k;
}
location ~ /\.ht {
deny all;
}
}
EOF
5. Start Invoice Ninja:
# Start services
docker-compose up -d
# Run migrations
docker-compose exec app php artisan migrate:fresh --seed
# Generate app key (if not set)
docker-compose exec app php artisan key:generate
# Optimize application
docker-compose exec app php artisan optimize
Manual Installation
1. Install system dependencies:
# Ubuntu/Debian
sudo apt update
sudo apt install -y \
nginx \
mariadb-server \
redis-server \
php8.1-fpm \
php8.1-mysql \
php8.1-curl \
php8.1-gd \
php8.1-mbstring \
php8.1-xml \
php8.1-zip \
php8.1-bcmath \
php8.1-intl \
php8.1-redis \
php8.1-imagick \
php8.1-gmp \
composer \
git \
curl \
unzip
2. Create database:
# Secure MariaDB
sudo mysql_secure_installation
# Create database and user
sudo mysql -u root -p << EOF
CREATE DATABASE invoiceninja;
CREATE USER 'invoiceninja'@'localhost' IDENTIFIED BY 'secure_password';
GRANT ALL PRIVILEGES ON invoiceninja.* TO 'invoiceninja'@'localhost';
FLUSH PRIVILEGES;
EXIT;
EOF
3. Download Invoice Ninja:
# Create directory
sudo mkdir -p /var/www/invoiceninja
cd /var/www/invoiceninja
# Download latest release
wget https://github.com/invoiceninja/invoiceninja/releases/latest/download/invoiceninja.zip
unzip invoiceninja.zip
rm invoiceninja.zip
# Set permissions
sudo chown -R www-data:www-data /var/www/invoiceninja
sudo chmod -R 755 /var/www/invoiceninja
sudo chmod -R 775 /var/www/invoiceninja/storage
sudo chmod -R 775 /var/www/invoiceninja/bootstrap/cache
4. Configure application:
# Copy environment file
cp .env.example .env
# Edit configuration
nano .env
# Generate application key
php artisan key:generate
# Run migrations
php artisan migrate:fresh --seed
# Optimize application
php artisan optimize
4. Configuration
Nginx Configuration
Create /etc/nginx/sites-available/invoiceninja
:
server {
listen 80;
server_name invoice.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name invoice.example.com;
ssl_certificate /etc/letsencrypt/live/invoice.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/invoice.example.com/privkey.pem;
root /var/www/invoiceninja/public;
index index.php;
client_max_body_size 50M;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_intercept_errors off;
fastcgi_buffer_size 16k;
fastcgi_buffers 4 16k;
}
location ~ /\.ht {
deny all;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
Enable site:
sudo ln -s /etc/nginx/sites-available/invoiceninja /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Queue Worker
Create /etc/systemd/system/invoiceninja-queue.service
:
[Unit]
Description=Invoice Ninja Queue Worker
After=network.target
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/var/www/invoiceninja
ExecStart=/usr/bin/php /var/www/invoiceninja/artisan queue:work --sleep=3 --tries=3 --timeout=90
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
Enable service:
sudo systemctl daemon-reload
sudo systemctl enable --now invoiceninja-queue.service
Cron Jobs
Add to crontab:
sudo crontab -e -u www-data
# Add this line
* * * * * cd /var/www/invoiceninja && php artisan schedule:run >> /dev/null 2>&1
Email Configuration
Configure in .env
:
# SMTP Settings
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=your-email@gmail.com
MAIL_PASSWORD=your-app-password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=noreply@invoice.example.com
MAIL_FROM_NAME="Invoice Ninja"
# Alternative: Mailgun
MAIL_MAILER=mailgun
MAILGUN_DOMAIN=mg.example.com
MAILGUN_SECRET=key-xxxxxxxxxxxxxxxxxxxxxxxx
MAILGUN_ENDPOINT=api.mailgun.net
# Alternative: SendGrid
MAIL_MAILER=sendgrid
SENDGRID_API_KEY=SG.xxxxxxxxxxxxxxxxxxxxxxxx
Payment Gateways
1. Stripe:
# In .env
STRIPE_PUBLIC_KEY=pk_live_xxxxx
STRIPE_SECRET_KEY=sk_live_xxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxx
2. PayPal:
# In .env
PAYPAL_CLIENT_ID=xxxxx
PAYPAL_SECRET=xxxxx
PAYPAL_MODE=live # or sandbox
3. Square:
# In .env
SQUARE_APPLICATION_ID=xxxxx
SQUARE_ACCESS_TOKEN=xxxxx
SQUARE_LOCATION_ID=xxxxx
Advanced Configuration
Multi-Company Setup
# Enable multi-company
MULTI_DB_ENABLED=true
# Configure per-company databases
DB_HOST1=db1.example.com
DB_DATABASE1=company1
DB_USERNAME1=user1
DB_PASSWORD1=pass1
DB_HOST2=db2.example.com
DB_DATABASE2=company2
DB_USERNAME2=user2
DB_PASSWORD2=pass2
Custom PDF Templates
1. Create custom design:
// resources/views/pdf/custom_invoice.blade.php
<!DOCTYPE html>
<html>
<head>
<style>
/* Custom CSS */
</style>
</head>
<body>
<!-- Custom HTML template -->
</body>
</html>
2. Register design:
php artisan make:design CustomInvoice
API Configuration
# Enable API
API_SECRET=your-api-secret
# Rate limiting
RATE_LIMIT_PER_MINUTE=60
# API version
API_VERSION=v1
Webhook Configuration
# Webhook URL
WEBHOOK_URL=https://your-app.com/webhooks/invoiceninja
# Webhook events
WEBHOOK_EVENTS=invoice.created,payment.completed,client.created
Security Configuration
Two-Factor Authentication
# Enable 2FA
ENABLE_2FA=true
# 2FA methods
TWO_FACTOR_METHODS=google,sms,email
IP Whitelisting
# Whitelist IPs
IP_WHITELIST=192.168.1.0/24,10.0.0.0/8
# Blacklist IPs
IP_BLACKLIST=1.2.3.4,5.6.7.8
Security Headers
Add to nginx configuration:
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' https: data: blob: wss:;" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
9. Backup and Restore
Backup Script
#!/bin/bash
# backup-invoiceninja.sh
BACKUP_DIR="/backup/invoiceninja/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"
# Backup database
mysqldump -u invoiceninja -p invoiceninja > "$BACKUP_DIR/database.sql"
# Backup files
tar czf "$BACKUP_DIR/files.tar.gz" \
-C /var/www/invoiceninja \
storage/app \
public/storage \
.env
# Backup nginx config
cp /etc/nginx/sites-available/invoiceninja "$BACKUP_DIR/"
echo "Backup completed: $BACKUP_DIR"
Restore Script
#!/bin/bash
# restore-invoiceninja.sh
BACKUP_DIR="/backup/invoiceninja/20231201_120000"
# Stop services
sudo systemctl stop nginx php8.1-fpm invoiceninja-queue
# Restore database
mysql -u invoiceninja -p invoiceninja < "$BACKUP_DIR/database.sql"
# Restore files
tar xzf "$BACKUP_DIR/files.tar.gz" -C /var/www/invoiceninja/
# Set permissions
sudo chown -R www-data:www-data /var/www/invoiceninja
sudo chmod -R 755 /var/www/invoiceninja
sudo chmod -R 775 /var/www/invoiceninja/storage
sudo chmod -R 775 /var/www/invoiceninja/bootstrap/cache
# Clear cache
cd /var/www/invoiceninja
php artisan cache:clear
php artisan config:clear
php artisan view:clear
# Start services
sudo systemctl start nginx php8.1-fpm invoiceninja-queue
echo "Restore completed from: $BACKUP_DIR"
Performance Optimization
PHP-FPM Tuning
Edit /etc/php/8.1/fpm/pool.d/www.conf
:
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 500
; Increase memory and execution limits
php_admin_value[memory_limit] = 256M
php_admin_value[max_execution_time] = 300
php_admin_value[post_max_size] = 50M
php_admin_value[upload_max_filesize] = 50M
Redis Optimization
# Configure Redis
sudo nano /etc/redis/redis.conf
# Add/modify:
maxmemory 1gb
maxmemory-policy allkeys-lru
save "" # Disable persistence for cache
Database Optimization
-- Optimize tables
OPTIMIZE TABLE clients, invoices, payments, products;
-- Add indexes
CREATE INDEX idx_invoices_client_id ON invoices(client_id);
CREATE INDEX idx_invoices_date ON invoices(invoice_date);
CREATE INDEX idx_payments_invoice_id ON payments(invoice_id);
Laravel Optimization
# Cache configuration
php artisan config:cache
php artisan route:cache
php artisan view:cache
# Optimize autoloader
composer install --optimize-autoloader --no-dev
# Enable OPcache
sudo phpenmod opcache
Monitoring
Health Checks
# Check application health
curl https://invoice.example.com/api/v1/health
# Check queue status
php artisan queue:monitor
# Check failed jobs
php artisan queue:failed
Logging
Configure in .env
:
# Log channel
LOG_CHANNEL=daily
# Log level
LOG_LEVEL=error
# Log retention
LOG_DEPRECATIONS_CHANNEL=null
LOG_DAYS=14
Application Monitoring
# Enable Laravel Telescope (development)
composer require laravel/telescope --dev
php artisan telescope:install
php artisan migrate
# Enable Sentry (production)
SENTRY_LARAVEL_DSN=https://xxxxx@sentry.io/xxxxx
6. Troubleshooting
Common Issues
1. 500 Internal Server Error:
# Check permissions
sudo chown -R www-data:www-data /var/www/invoiceninja
sudo chmod -R 755 /var/www/invoiceninja
sudo chmod -R 775 /var/www/invoiceninja/storage
sudo chmod -R 775 /var/www/invoiceninja/bootstrap/cache
# Clear cache
php artisan cache:clear
php artisan config:clear
php artisan view:clear
2. PDF generation issues:
# Install required packages
sudo apt install -y chromium-browser
# Configure in .env
PDF_GENERATOR=snappdf
SNAPPDF_EXECUTABLE_PATH=/usr/bin/chromium-browser
3. Email not sending:
# Test email configuration
php artisan tinker
>>> Mail::raw('Test email', function($message) {
... $message->to('test@example.com')->subject('Test');
... });
# Check queue
php artisan queue:work --once
Debug Mode
Enable debug mode (development only):
# In .env
APP_DEBUG=true
APP_ENV=local
# View logs
tail -f storage/logs/laravel.log
Upgrade Process
# Backup first!
./backup-invoiceninja.sh
# Put application in maintenance mode
php artisan down
# Pull latest code
git fetch --tags
git checkout tags/v5.x.x
# Update dependencies
composer install --no-dev
npm install
npm run production
# Run migrations
php artisan migrate --force
# Clear caches
php artisan cache:clear
php artisan config:cache
php artisan view:cache
# Bring application back online
php artisan up
Additional Resources
---
Note: This guide is part of the HowToMgr collection. Always refer to official documentation for the most up-to-date information.