Dacă emiți facturi recurente lună de lună, ai doar două variante pe masă. Fie decartezi un abonament lunar unui SaaS „în cloud” care îți va ține ostatică baza de clienți, istoricul financiar și toate PDF-urile pe serverele lor. Fie îți instalezi Invoice Ninja pe un VPS controlat 100% de tine, nu mai dai un ban pe licențe software și păstrezi controlul absolut asupra propriilor facturi.
Invoice Ninja e un monstru open-source scris în Laravel, care știe să facă absolut tot: oferte, proforme, plăți recurente, tracking pe task-uri și integrări directe cu gateway-urile de plată. Varianta self-hosted e gratuită la nivel de funcționalități, fără branding ascuns și limitări artificiale. Singurul tău cost este efectiv plata lunară a VPS-ului.
În acest tutorial, îți arăt exact cum să pui pe picioare un stack de Docker Compose solid, gândit direct pentru producție. La final vei avea propriul sistem de billing, cu domeniu personalizat, TLS configurat, backup-uri funcționale și trimitere de emailuri prin SMTP.
De ce să te complici cu un sistem de facturare self-hosted
SaaS-urile de facturare te taxează lunar, iar prețul scalează odată cu numărul de clienți. Pentru un freelancer cu o mână de colaboratori, e absurd să plătești abonamente grase doar ca să generezi niște PDF-uri. Self-hosting-ul taie acel abonament direct din schemă și îl înlocuiește cu costul fix al unui VPS pe care, foarte probabil, îl ai deja pornit pentru alte servicii.
Marele avantaj, însă, este proprietatea datelor. Când folosești soluții third-party, baza ta de clienți stă la mila unui provider care oricând poate fi cumpărat de altcineva sau își poate modifica agresiv Terms of Service-ul. Prin self-hosting, totul stă pe SSD-ul tău. Mai mult, pentru freelancerii din UE, asta îți face viața mult mai ușoară cu GDPR-ul: știi clar unde sunt stocate fizic datele și cine are (sau nu) acces la ele.
Dacă folosești deja un VPS pentru site-ul tău de portofoliu sau ai ridicat alte mici utilitare pentru casă, să adaugi Invoice Ninja te costă fix zero lei. Un VPS KVM ServerSpan de 2 GB RAM ține stack-ul ăsta fără probleme, alături de un reverse proxy și alte containere mai mici.
De ce ai nevoie
- Un VPS cu măcar 2 vCPU și 2 GB RAM. Stack-ul combinat (Invoice Ninja, MariaDB, Redis) va consuma în jur de 1.2 GB când nu face nimic.
- Docker și pluginul de Docker Compose.
- Un nume de domeniu (ex.
facturare.domeniultau.ro) pointat direct către IP-ul VPS-ului tău. - Un reverse proxy (Traefik sau Caddy) care să rezolve terminarea TLS. Dacă abia acum începi și ai nevoie de un setup curat, ghidul nostru de self-hosting pentru website îți arată cum să configurezi de la zero VPS-ul, firewall-ul și certificatele SSL.
Setup-ul de Docker Compose
Invoice Ninja ne pune la dispoziție imagini oficiale de Docker. Pentru v5, ai nevoie de o arhitectură cu 4 piese: containerul principal al aplicației, un Nginx care îi servește fișierele statice, baza de date MariaDB și un instanță de Redis pentru cache și queue-uri.
Structura directoarelor
Intră prin SSH pe server, creează folderul stack-ului și navighează în el:
mkdir -p /opt/invoiceninja && cd /opt/invoiceninja
Generează cheia aplicației
Invoice Ninja are nevoie de un șir aleatoriu de caractere pentru a-ți cripta datele din baza de date. Generează cheia înainte să scrii configurațiile:
openssl rand -base64 32
Copiază output-ul, pentru că o să-l punem imediat la APP_KEY în fișierul de environment.
Fișierul docker-compose.yml
services:
app:
image: invoiceninja/invoiceninja:5
restart: unless-stopped
env_file: .env
volumes:
- ./public:/var/www/app/public:rw
- ./storage:/var/www/app/storage:rw
depends_on:
- db
- redis
networks:
- invoiceninja
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- "127.0.0.1:8000:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./public:/var/www/app/public:ro
- ./storage:/var/www/app/storage:ro
depends_on:
- app
networks:
- invoiceninja
db:
image: mariadb:10.11
restart: unless-stopped
env_file: .env
volumes:
- db_data:/var/lib/mysql
networks:
- invoiceninja
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- redis_data:/data
networks:
- invoiceninja
volumes:
db_data:
redis_data:
networks:
invoiceninja:
Atenție la o nuanță critică aici: Nginx face bind direct pe 127.0.0.1:8000 și nu pe un banal 0.0.0.0:80. Astfel, aplicația va asculta strict traficul din localhost-ul VPS-ului, lăsând reverse proxy-ul (Traefik sau Caddy) să îi aducă traficul de pe web. Niciodată nu e o idee bună să ții aplicația PHP expusă direct în bătaia port scanerelor de pe internet.
Fișierul .env
APP_URL=https://facturare.domeniultau.ro APP_KEY=base64:CHEIA_GENERATA_AICI APP_DEBUG=false DB_HOST=db DB_PORT=3306 DB_DATABASE=ninja DB_USERNAME=ninja DB_PASSWORD=parola_puternica_aici DB_ROOT_PASSWORD=parola_root_puternica_aici MAIL_MAILER=smtp MAIL_HOST=smtp.sendgrid.net MAIL_PORT=587 MAIL_USERNAME=apikey MAIL_PASSWORD=cheia_api_sendgrid [MAIL_FROM_ADDRESS=facturare@domeniultau.ro](mailto:MAIL_FROM_ADDRESS=facturare@domeniultau.ro) MAIL_FROM_NAME="Numele Tău" REDIS_HOST=redis REDIS_PORT=6379
Înlocuiește manual toți parametrii. APP_URL trebuie să fie fix domeniul pe care l-ai configurat în DNS. De asemenea, ai grijă ca parolele de MariaDB să fie solide.
HTTPS-ul prin reverse proxy
Dacă mergi pe un setup de producție corect, reverse proxy-ul trebuie să preia certificatul SSL de la Let's Encrypt. Dacă folosești Traefik, lipește label-urile astea pe containerul nginx din Compose:
labels: - "traefik.enable=true" - "traefik.http.routers.invoiceninja.rule=Host(`facturare.domeniultau.ro`)" - "traefik.http.routers.invoiceninja.tls.certresolver=letsencrypt" - "traefik.http.services.invoiceninja.loadbalancer.server.port=8000"
Dacă te-ai dus pe ruta de Caddy, blocul tău din Caddyfile e mult mai banal:
facturare.domeniultau.ro {
reverse_proxy localhost:8000
}
Prima lansare și setup-ul inițial
Dă-i drumul la treabă:
docker compose up -d
Sistemul va avea nevoie de aproximativ 30 de secunde să creeze tabelele în baza de date la prima rulare. După ce trece timpul, intră din browser pe https://facturare.domeniultau.ro. Te va întâmpina interfața curată a wizard-ului Invoice Ninja.
Ce trebuie să faci la pasul ăsta:
- Setează datele de bază: numele firmei, adresa de login și o parolă sănătoasă.
- Fixează corect fusul orar și moneda de referință.
- Sari momentan peste testarea contului SMTP; o s-o configurăm manual în secțiunea următoare.
Dacă vezi că wizard-ul agață și dă eroare la baza de date, buba e 100% în fișierul .env. Verifică iarăși utilizatorul și parola și dă un docker compose ps ca să fii sigur că containerul de db a primit statusul de healthy.
Cum rezolvi trimiterea facturilor (SMTP)
Invoice Ninja e degeaba dacă nu poate trimite mailurile cu facturile și chitanțele. E obligatoriu să-i legi un serviciu SMTP stabil.
Atenție mare aici: Dacă folosești un VPS ServerSpan (la fel ca la majoritatea furnizorilor de top), portul 25 pentru trimitere de mail este blocat by default pentru a preveni abuzurile de spam. Din fericire, asta nu e o problemă, pentru că niciun profesionist nu trimite mailuri neautentificate. Trebuie să folosești porturile securizate (587 sau 465). De altfel, a-ți configura de la zero un Postfix ca să trimiți 5 facturi e sinucidere curată pentru livrabilitate.
Alege oricare dintre serviciile tranzacționale de mai jos:
- SendGrid: Ai 100 de mailuri gratuite pe zi, perfect pentru freelancing.
- Postmark: Cel mai rapid, ajunge garantat în inbox.
- Brevo (fost Sendinblue): Permisiv pe limitele gratuite de volum mic.
- Mailgun: Excelent, dar mult mai axat pe agențiile care trimit volume mari prin API.
După ce îți pui setările în .env, dă un restart la instanța PHP:
docker compose restart app
Intră înapoi în aplicație, la Settings > Email Settings și dă un test. Dacă ajunge în inbox, ești gata.
Dacă Invoice Ninja generează PDF-uri goale
Invoice Ninja scoate facturile în format PDF folosind wkhtmltopdf. Imaginea de Docker livrează acest binar, dar uneori mai dă chix din cauza actualizărilor. Dacă clienții îți spun că văd doar un fișier PDF alb sau pur și simplu primești o eroare silențioasă la export, 99% din timp binarul este de vină.
Testează să vezi dacă binarul stă în picioare în interiorul containerului:
docker compose exec app wkhtmltopdf --version
Dacă aruncă o eroare urâtă sau versiunea raportată e crunt de veche, ai două opțiuni. Fie repari imaginea (rebuild manual cu o versiune fixată), fie te duci în dashboard-ul Invoice Ninja și schimbi motorul de PDF pe „Hosted”. Varianta Hosted trimite HTML-ul facturii către serverele lor doar pentru randare; funcționează perfect și te scutește de troubleshooting, dar dacă ții cu dinții de confidențialitatea strictă, va trebui să repari binarul tău local.
Automatizarea facturilor (Cron jobs)
Dacă nu activezi cronul, Invoice Ninja e mort la capitolul facturi recurente și reminder-e de plată întârziată. Schedulerele din ecosistemul Laravel au nevoie să fie „împinse” de la spate regulat. Ca să facem asta curat pe Docker, punem un cron direct pe OS-ul VPS-ului, care va chema comanda în interiorul containerului.
Intră pe contul de root și editează crontab-ul:
sudo crontab -e
Adaugă linia asta și salvează:
* * * * * cd /opt/invoiceninja && docker compose exec -T app php artisan schedule:run >> /dev/null 2>&1
Acum VPS-ul tău va da câte un ping la fiecare minut către Invoice Ninja, iar sistemul se va ocupa de trimis restanțele și rapoartele la intervalele corecte.
Cum faci backup ca un profesionist
Datele tale financiare și design-urile facturilor stau pe disk în doar două locuri: volumul bazei de date (MariaDB) și directorul storage. Dacă îți pică VPS-ul sau execuți din greșeală o comandă letală, și nu ai un backup testat pentru ele, poți să închizi laptopul și să te duci acasă.
Cel mai brutal și rapid backup arată cam așa (dump la DB și un tar peste fișiere):
docker compose exec db mariadb-dump -u ninja -p ninja > invoiceninja-backup-$(date +%F).sql tar czf invoiceninja-storage-$(date +%F).tar.gz storage/
Asta e bine pentru teste, dar nu și pentru sleep-well at night. Dacă vrei automatizare de top cu deduplicare pe servere mici, citește comparația dintre BorgBackup și Restic. Te învățăm acolo cum să extragi arhiva criptată zilnic și să o arunci într-un S3 extern fără ca procesul de pruning să consume tot RAM-ul și să îți dea OOM kill pe server.
Regula de aur: Faci backup zilnic. Faci un restore de test lunar. Un backup pe care nu l-ai verificat dacă se extrage corect nu se cheamă backup, e doar un fișier care mănâncă spațiu degeaba.
La ce să te aștepți ca performanță pe un VPS mic
Invoice Ninja e un „greu” al framework-ului PHP Laravel. Nu e o aplicație de tip Go scrisă minimal, dar e extrem de rezonabilă pe resurse dacă înțelegi ce face în spate.
| Serviciu | RAM idle | RAM în sarcină (peak) |
|---|---|---|
| MariaDB | 300 MB | 600 MB |
| Invoice Ninja (app) | 250 MB | 500 MB |
| Redis | 20 MB | 100 MB |
| Nginx | 10 MB | 30 MB |
| Total | ~580 MB | ~1.2 GB |
Când doar te uiți la dashboard, stai pe la 600 MB ocupați. Când dai comenzi grele (export de zeci de CSV-uri, generare asincronă de PDF-uri sau lovituri pe API-ul de la Stripe), memoria sare peste suta de MB suplimentari pe request.
Aici, un VPS ServerSpan din categoria ct.Ready (2 Core-uri, 2 GB RAM) este fix baza. Duce sistemul fără să crâcnească, dar nu îți mai lasă prea mult spațiu dacă vrei să arunci și alte aplicații mari de self-hosting pe același server. Dacă pui limite tari de memorie în Docker Compose și vezi că aplicația dă timeout-uri, planul corect este mutarea pe gama ct.Steady (4 Core-uri, 4 GB RAM).
FAQ - Întrebări pe care ni le pun freelancerii
De ce m-aș obosi cu asta în loc să-mi fac cont pe FreshBooks sau Wave?
| Diferențe majore | Invoice Ninja (Self-hosted) | FreshBooks | Wave |
|---|---|---|---|
| Prețul tău pe lună | E gratis (dai banii doar pe VPS) | Abonament la lună | Gratuit, dar cu taxe mari pe tranzacții |
| Cine controlează datele? | Doar tu (100%) | Providerul de cloud | Providerul de cloud |
| White-label (fără logo lor) | Poți cumpăra un add-on ieftin | Se poate doar la abonamentele Premium | Nu există opțiunea |
| Facturi recurente? | Da | Da | Da |
| Procesoare de plată acceptate | Stripe, PayPal, Mollie și alte 40+ | O mână de servicii selectate | Te forțează exclusiv pe procesorul lor |
| Suportă API extern? | Acces complet la tot | Extrem de limitat | Deloc |
Matematica e simplă. Un SaaS te scapă de instalarea inițială, dar te taxează la sânge în fiecare lună și se joacă cu datele tale. Invoice Ninja instalat de tine te costă niște efort la început, apoi rulează pe cheltuiala minimă de VPS, păstrând totul în curtea ta.
Pot să-mi mut clienții din versiunea de cloud direct în VPS-ul meu?
Da, fără probleme. Ai export direct în JSON din dashboard-ul cloud și import la 1:1 în panoul de administrare pe care l-ai instalat. Fă-ți doar o favoare și rulează un test pe un server de staging prima oară, ca să nu strici indicii și counter-ele la facturi dacă te-ai jucat cu ele în noua instanță.
Chiar trebuie să cumpăr licența lor?
Categoric nu. Modulul e 100% funcțional gratuit. Licența de „White-Label” costă cam treizeci de dolari anual și singura chestie pe care o face e să scoată textul de „Created with Invoice Ninja” de pe fundul PDF-ului pe care îl trimiți clienților. Dacă facturezi pentru proiecte mari și vrei ca tot procesul să pară dezvoltat intern, o cumperi. Altfel, e strict opțională.
Mi s-a oprit trimiterea de emailuri dintr-o dată. Ce a crăpat?
Fără panică, ia-le logic la puricat:
- Cineva s-a jucat prin fișierul
.env. Verifică setările SMTP și mai dă o dată undocker compose restart app. - Ai luat rate-limit de la serviciul tău. Loghează-te în SendGrid/Postmark și vezi ce erori afișează ei.
- Ai stricat setările de DNS (înregistrările de tip SPF și DKIM) când te-ai atins de domenii, iar mailurile tale ajung instant la spam-ul clientului.
Când e timpul să scalezi hardware-ul (Scale-Up)
Dacă îți gestionezi singur SRL-ul sau PFA-ul cu 15-20 de clienți pe lună, o instanță de 2 GB de RAM își face lejer treaba. Dar în clipa în care îți pui echipa în dashboard, conectezi un CRM la API și rulezi un volum gros de PDF-uri, baza de date MariaDB o să gâfâie din toate puterile.
Cum îți dai seama că ceri prea mult de la VPS-ul tău:
- Când apeși „Export PDF”, roata se învârte pe ecran mai mult de 10-15 secunde.
- Aplicația se încarcă în reluare chiar și cu Redis configurat perfect pe cache.
- În
docker psobservi că instanțaappa făcut restart-uri aleatoare din cauza unui tăcut OOM kill. - Alte containere (de exemplu portofoliul tău WordPress) găzduite pe același IP încep să dea timeout-uri 502/504.
Singura reparație sustenabilă pe o platformă self-hosted e să faci upgrade la hardware. Nu activa swap în ideea că poți dribla limitările; tot ce faci e să îți distrugi viteza de citire a SSD-ului, mascând o problemă evidentă de performanță a aplicației.
Să deții controlul afacerii tale înseamnă să gestionezi complet locul din care pleacă facturile și locul unde se stochează detaliile clienților tăi. Punând Invoice Ninja pe propriul tău server dedicat virtual (VPS), obții libertatea financiară și securitatea datelor cu zero compromisuri tehnice.
Sursă și Atribuire
Aceast articol se bazează pe date originale ale serverspan.com. Pentru metodologia completă și pentru a asigura integritatea datelor, articolul original trebuie citat. Sursa canonică este disponibilă la: Invoice Ninja pe VPS propriu: sistem de facturare pentru freelanceri.