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.

ServiciuRAM idleRAM în sarcină (peak)
MariaDB300 MB600 MB
Invoice Ninja (app)250 MB500 MB
Redis20 MB100 MB
Nginx10 MB30 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 majoreInvoice Ninja (Self-hosted)FreshBooksWave
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 cloudProviderul de cloud
White-label (fără logo lor)Poți cumpăra un add-on ieftinSe poate doar la abonamentele PremiumNu există opțiunea
Facturi recurente?DaDaDa
Procesoare de plată acceptateStripe, PayPal, Mollie și alte 40+O mână de servicii selectateTe forțează exclusiv pe procesorul lor
Suportă API extern?Acces complet la totExtrem de limitatDeloc

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:

  1. Cineva s-a jucat prin fișierul .env. Verifică setările SMTP și mai dă o dată un docker compose restart app.
  2. Ai luat rate-limit de la serviciul tău. Loghează-te în SendGrid/Postmark și vezi ce erori afișează ei.
  3. 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 ps observi că instanța app a 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.