198 lines
5.9 KiB
Markdown
198 lines
5.9 KiB
Markdown
|
|
# Invoice Ninja
|
||
|
|
|
||
|
|
**Type**: Invoicing & Billing Application
|
||
|
|
**Domain**: https://invoice.hyperthrive.io
|
||
|
|
**Version**: 5.12.28
|
||
|
|
**Architecture**: Official Docker stack from [invoiceninja/dockerfiles](https://github.com/invoiceninja/dockerfiles)
|
||
|
|
**Deployment Date**: 2025-10-14
|
||
|
|
|
||
|
|
## Architecture
|
||
|
|
|
||
|
|
Invoice Ninja runs using the official Docker Compose stack with three services:
|
||
|
|
|
||
|
|
1. **invoiceninja-server-1** - Nginx web server (port 80 internal)
|
||
|
|
2. **invoiceninja-app-1** - Laravel application with supervisord (PHP-FPM, queue workers, scheduler)
|
||
|
|
3. **invoiceninja-db-1** - MariaDB 10.11 database
|
||
|
|
|
||
|
|
### Traefik Integration
|
||
|
|
|
||
|
|
The nginx service connects to the external `traefik` network and uses labels for automatic SSL/routing:
|
||
|
|
|
||
|
|
- **Domain**: invoice.hyperthrive.io
|
||
|
|
- **Protocol**: HTTPS via Let's Encrypt
|
||
|
|
- **Entry Point**: websecure (443)
|
||
|
|
- **Internal Port**: 80 (nginx)
|
||
|
|
|
||
|
|
### Directory Structure (Server)
|
||
|
|
|
||
|
|
```
|
||
|
|
~/services/invoiceninja/
|
||
|
|
├── docker-compose.yml # Main compose file
|
||
|
|
├── env # Environment configuration
|
||
|
|
├── config/
|
||
|
|
│ └── nginx/
|
||
|
|
│ └── in-vhost.conf # Nginx vhost config
|
||
|
|
├── docker/
|
||
|
|
│ ├── app/
|
||
|
|
│ │ ├── public/ # Application public files (shared with nginx)
|
||
|
|
│ │ └── storage/ # Application storage (logs, cache, uploads)
|
||
|
|
│ └── mysql/
|
||
|
|
│ └── data/ # Database files
|
||
|
|
```
|
||
|
|
|
||
|
|
**Important**: The `docker/app/` directories must be owned by user `1500:1500` (the container's runtime user).
|
||
|
|
|
||
|
|
## Common Operations
|
||
|
|
|
||
|
|
### Check Status
|
||
|
|
|
||
|
|
```bash
|
||
|
|
ssh jared@15.204.247.153 "docker ps --filter name=invoiceninja"
|
||
|
|
```
|
||
|
|
|
||
|
|
### View Logs
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Application logs
|
||
|
|
ssh jared@15.204.247.153 "docker logs invoiceninja-app-1 --tail 50"
|
||
|
|
|
||
|
|
# Database logs
|
||
|
|
ssh jared@15.204.247.153 "docker logs invoiceninja-db-1 --tail 50"
|
||
|
|
|
||
|
|
# Nginx logs
|
||
|
|
ssh jared@15.204.247.153 "docker logs invoiceninja-server-1 --tail 50"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Update Invoice Ninja
|
||
|
|
|
||
|
|
```bash
|
||
|
|
ssh jared@15.204.247.153 "cd ~/services/invoiceninja && \
|
||
|
|
docker compose pull && \
|
||
|
|
docker compose up -d && \
|
||
|
|
docker exec invoiceninja-app-1 php artisan migrate --force && \
|
||
|
|
docker exec invoiceninja-app-1 php artisan optimize"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Backup Database
|
||
|
|
|
||
|
|
```bash
|
||
|
|
ssh jared@15.204.247.153 "cd ~/services/invoiceninja && \
|
||
|
|
docker compose exec -T db sh -c 'exec mysqldump --all-databases -uroot -p\"\$MYSQL_ROOT_PASSWORD\"' \
|
||
|
|
> ~/backups/invoiceninja-$(date +%Y%m%d-%H%M%S).sql"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Restore Database
|
||
|
|
|
||
|
|
```bash
|
||
|
|
ssh jared@15.204.247.153 "cd ~/services/invoiceninja && \
|
||
|
|
docker compose exec -T db sh -c 'exec mysql -uroot -p\"\$MYSQL_ROOT_PASSWORD\"' \
|
||
|
|
< ~/backups/invoiceninja-TIMESTAMP.sql"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Restart Services
|
||
|
|
|
||
|
|
```bash
|
||
|
|
ssh jared@15.204.247.153 "cd ~/services/invoiceninja && docker compose restart"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Run Artisan Commands
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Generic pattern
|
||
|
|
ssh jared@15.204.247.153 "docker exec invoiceninja-app-1 php artisan <command>"
|
||
|
|
|
||
|
|
# Examples
|
||
|
|
ssh jared@15.204.247.153 "docker exec invoiceninja-app-1 php artisan queue:work"
|
||
|
|
ssh jared@15.204.247.153 "docker exec invoiceninja-app-1 php artisan cache:clear"
|
||
|
|
ssh jared@15.204.247.153 "docker exec invoiceninja-app-1 php artisan db:seed"
|
||
|
|
```
|
||
|
|
|
||
|
|
## Key Configuration
|
||
|
|
|
||
|
|
### Environment Variables (env file)
|
||
|
|
|
||
|
|
Critical settings in `~/services/invoiceninja/env`:
|
||
|
|
|
||
|
|
- `APP_URL=https://invoice.hyperthrive.io` - Public URL
|
||
|
|
- `APP_KEY=base64:...` - Encryption key (DO NOT change after initial setup)
|
||
|
|
- `DB_HOST=db` - Database hostname (container name)
|
||
|
|
- `MYSQL_ROOT_PASSWORD` - DB root password
|
||
|
|
- `MYSQL_DATABASE=ninja` - Database name
|
||
|
|
- `MYSQL_USER=ninja` - DB user
|
||
|
|
- `MYSQL_PASSWORD` - DB password
|
||
|
|
- `TRUSTED_PROXIES=*` - Trust Traefik proxy
|
||
|
|
- `MAIL_MAILER=log` - Mail driver (currently logging only)
|
||
|
|
|
||
|
|
### Traefik Labels (docker-compose.yml)
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
labels:
|
||
|
|
- "traefik.enable=true"
|
||
|
|
- "traefik.http.routers.invoiceninja.rule=Host(`invoice.hyperthrive.io`)"
|
||
|
|
- "traefik.http.routers.invoiceninja.entrypoints=websecure"
|
||
|
|
- "traefik.http.routers.invoiceninja.tls.certresolver=letsencrypt"
|
||
|
|
- "traefik.http.services.invoiceninja.loadbalancer.server.port=80"
|
||
|
|
- "traefik.docker.network=traefik"
|
||
|
|
```
|
||
|
|
|
||
|
|
## Troubleshooting
|
||
|
|
|
||
|
|
### Permission Denied Errors
|
||
|
|
|
||
|
|
If the app container fails with "Permission denied" creating directories:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
ssh jared@15.204.247.153 "cd ~/services/invoiceninja && \
|
||
|
|
docker compose down && \
|
||
|
|
sudo chown -R 1500:1500 docker/app && \
|
||
|
|
docker compose up -d"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Database Connection Issues
|
||
|
|
|
||
|
|
Check database is healthy:
|
||
|
|
```bash
|
||
|
|
ssh jared@15.204.247.153 "docker inspect invoiceninja-db-1 --format='{{.State.Health.Status}}'"
|
||
|
|
```
|
||
|
|
|
||
|
|
Should return `healthy`. If not, check logs.
|
||
|
|
|
||
|
|
### 502 Bad Gateway
|
||
|
|
|
||
|
|
Usually means the app container isn't running or PHP-FPM hasn't started:
|
||
|
|
|
||
|
|
1. Check app container status: `docker ps --filter name=invoiceninja-app-1`
|
||
|
|
2. Check app logs: `docker logs invoiceninja-app-1 --tail 100`
|
||
|
|
3. Look for PHP-FPM startup message: "ready to handle connections"
|
||
|
|
|
||
|
|
### SSL Certificate Issues
|
||
|
|
|
||
|
|
Traefik automatically handles SSL. If certificate issues occur:
|
||
|
|
|
||
|
|
1. Check Traefik logs: `docker logs traefik`
|
||
|
|
2. Verify DNS points to server: `dig invoice.hyperthrive.io`
|
||
|
|
3. Check Traefik dashboard for router/certificate status
|
||
|
|
|
||
|
|
### Queue Not Processing
|
||
|
|
|
||
|
|
The official stack includes queue workers via supervisord. Check they're running:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
ssh jared@15.204.247.153 "docker exec invoiceninja-app-1 supervisorctl status"
|
||
|
|
```
|
||
|
|
|
||
|
|
Should show `queue-worker_00` and `queue-worker_01` as RUNNING.
|
||
|
|
|
||
|
|
## Migration History
|
||
|
|
|
||
|
|
This deployment uses the **official Invoice Ninja Docker stack** (as of 2025-10-14). Previous custom stack was replaced to align with upstream recommendations and simplify maintenance.
|
||
|
|
|
||
|
|
**Pre-migration backup location**: `~/backups/invoiceninja-pre-official/` on server (retained until deployment proven stable)
|
||
|
|
|
||
|
|
## References
|
||
|
|
|
||
|
|
- Official Docker Repository: https://github.com/invoiceninja/dockerfiles
|
||
|
|
- Invoice Ninja Docs: https://invoiceninja.github.io
|
||
|
|
- Server Profile: `profile.yaml`
|
||
|
|
- Configuration Template: `configs/invoiceninja-official-template.yml`
|