modul 00 / de la shared la server propriu
ce înseamnă cu adevărat un server propriu
Pe shared hosting, furnizorul se ocupă de tot: sistem de operare, actualizări de securitate, configurare, backup. Tu ai doar un panou. Pe un VPS primești un server gol și acces root — control total. Odată cu el vine și responsabilitatea totală.
ce câștigi
- Control complet — orice versiune, orice extensie, orice serviciu.
- Resurse garantate — nu le împarți cu vecini imprevizibili.
- Performanță reglabilă — configurezi fiecare strat pentru cazul tău.
- Costuri predictibile la scară — un VPS decent costă cât un shared bun, dar duce mult mai mult.
- Competențe reale — înțelegi ce se întâmplă, nu doar apeși butoane.
ce îți asumi
| Responsabilitate | Ce înseamnă concret |
| Actualizări de securitate | Dacă nu le aplici tu, nu le aplică nimeni |
| Configurare | Server web, baze de date, certificate, firewall |
| Backup | Nu există „restaurare dintr-un clic" implicită |
| Monitorizare | Afli că a căzut doar dacă ai pus tu ceva să te anunțe |
| Depanare | La 3 dimineața, tu ești suportul tehnic |
întrebarea onestă înainte de a începe
Un VPS neîntreținut e mai periculos decât un shared hosting mediocru: rulează versiuni vechi, cu vulnerabilități publice, fără nimeni care să aplice corecții. Multe servere compromise sunt VPS-uri configurate entuziast acum doi ani și uitate de atunci.
Întreabă-te: voi aloca timp lunar pentru întreținere? Dacă răspunsul e nu, un hosting administrat (managed) e alegerea mai responsabilă — și acesta e un răspuns perfect legitim.
când merită trecerea
- Ai nevoie de software pe care shared-ul nu-l permite (versiuni, servicii, procese persistente).
- Aplicația ta nu e PHP clasic (Node, Python, Go) și îți trebuie procese care rulează continuu.
- Traficul depășește limitele shared-ului sau ai nevoie de performanță constantă.
- Vrei automatizare reală: publicare din Git, medii separate, containere.
- Vrei să înveți — motiv perfect valid, dacă nu e un site critic.
de reținut
Un VPS îți dă control total și resurse garantate, dar și responsabilitate totală: actualizări, configurare, backup, monitorizare, depanare. Un VPS neîntreținut e mai riscant decât un shared mediocru — cele mai multe servere compromise sunt cele configurate entuziast și apoi uitate. Treci la VPS când ai nevoie reală de control sau de procese persistente, și doar dacă îți asumi întreținerea.
modul 00 / de la shared la server propriu
alegerea & pregătirea serverului
ce dimensiune alegi
| Configurație | Potrivită pentru |
| 1 vCPU / 1–2 GB RAM | Un site mic, mediu de test, învățare |
| 2 vCPU / 4 GB RAM | Câteva site-uri, aplicație cu bază de date |
| 4 vCPU / 8 GB RAM | Trafic serios, mai multe servicii, containere |
RAM-ul e de obicei limitarea reală, nu procesorul — o bază de date și un server de aplicație consumă memorie constant. Începe modest: majoritatea furnizorilor permit mărirea resurselor ulterior, dar micșorarea e adesea imposibilă.
criterii care contează
- Locația — cât mai aproape de utilizatori (Europa pentru public din Moldova/România).
- Stocare NVMe/SSD, nu HDD.
- Snapshot-uri — copii ale întregului server, la un clic. Extrem de utile înainte de modificări riscante.
- Rețea privată — dacă vei avea mai multe servere care comunică.
- Trafic inclus — verifică limita și costul depășirii.
alegerea sistemului de operare
Pentru majoritatea cazurilor: o distribuție Linux cu suport pe termen lung (LTS) — Ubuntu LTS sau Debian stabil. Motivul e simplu: primesc actualizări de securitate ani în șir, iar documentația și răspunsurile online sunt abundente. Evită versiunile „cele mai noi" pe servere de producție.
snapshot înainte de orice
Prima deprindere pe care merită să o formezi: fă un snapshot înainte de orice modificare majoră — actualizare de sistem, schimbare de configurare, instalare de servicii noi. Costă câțiva cenți și îți poate salva ore. Un server stricat cu snapshot e o problemă de cinci minute; fără snapshot, poate fi o reinstalare completă.
primii pași după creare
Aceștia sunt pașii de bază; îi vom detalia în modulele următoare:
- Conectare prin SSH și actualizarea completă a sistemului.
- Creare utilizator obișnuit cu drepturi de administrare — nu lucrezi ca root.
- Autentificare cu chei și dezactivarea parolelor.
- Firewall pornit, cu doar porturile necesare deschise.
- Fus orar și nume de gazdă configurate.
- Actualizări automate de securitate activate.
Aceste șase lucruri, făcute în prima jumătate de oră, elimină majoritatea riscurilor cu care se confruntă serverele expuse pe internet.
de reținut
Alege un VPS cu RAM suficient (limitarea reală), aproape de utilizatori, cu snapshot-uri disponibile, pe o distribuție LTS — nu pe cea mai nouă versiune. Formează-ți reflexul: snapshot înainte de orice modificare majoră. Iar în prima jumătate de oră după creare: actualizezi, creezi un utilizator obișnuit, treci pe chei SSH, pornești firewall-ul și activezi actualizările automate de securitate.
modul 01 / Linux esențial pentru servere
sistemul de fișiere & comenzi zilnice
Nu ai nevoie să devii expert Linux, dar trebuie să te miști confortabil în terminal. Iată strictul necesar, organizat pe ce vei folosi efectiv.
unde stau lucrurile
| Cale | Ce conține |
| /etc | Fișiere de configurare ale sistemului și serviciilor |
| /var/www | Site-urile (convenție) |
| /var/log | Jurnale — primul loc unde cauți când ceva nu merge |
| /home/utilizator | Folderul personal |
| /opt, /srv | Software și date suplimentare |
| /tmp | Temporar — se poate șterge la repornire |
navigare & fișiere
pwd # unde sunt
ls -lah # listare detaliată, inclusiv fișiere ascunse
cd /var/www # schimb folderul
cp -r sursa dest # copiez recursiv
mv vechi nou # mut sau redenumesc
rm -rf folder # ȘTERGE definitiv, fără confirmare
du -sh * # cât ocupă fiecare element
df -h # spațiu liber pe discuri
rm -rf — comanda care nu întreabă
Nu există coș de gunoi și nu există confirmare. rm -rf /var/www șterge tot, instantaneu și definitiv. Obiceiuri care te salvează: rulează întâi ls pe aceeași cale ca să vezi ce vei șterge; nu folosi variabile neverificate în calea de ștergere; și, în caz de dubiu, mută în /tmp în loc să ștergi.
căutare & conținut
cat fisier # afișez tot
less fisier # parcurg pagină cu pagină (q = ieșire)
tail -f /var/log/nginx/error.log # urmăresc în timp real
grep -r "eroare" /var/log # caut text recursiv
find /var/www -name "*.log" # caut fișiere
nano fisier # editor simplu (Ctrl+O salvez, Ctrl+X ies)
tail -f pe jurnalul de erori, într-un terminal separat, în timp ce reproduci problema — e cea mai eficientă tehnică de depanare pe server. Vezi eroarea în momentul în care se produce.
pachete & servicii
sudo apt update && sudo apt upgrade # actualizez sistemul
sudo apt install nginx # instalez
sudo systemctl status nginx # în ce stare e
sudo systemctl restart nginx # repornesc
sudo systemctl reload nginx # reîncarc configurarea fără întrerupere
sudo systemctl enable nginx # pornire automată la boot
Distincția restart vs reload contează: reload reîncarcă configurarea fără a întrerupe conexiunile active. Pe producție, preferi reload ori de câte ori e posibil.
de reținut
Reține unde se află lucrurile (/etc configurări, /var/log jurnale, /var/www site-uri) și comenzile zilnice. Cea mai utilă tehnică de depanare: tail -f pe jurnalul de erori în timp ce reproduci problema. Tratează rm -rf cu maximă precauție — nu întreabă și nu iartă. Iar pe servicii, folosește reload în loc de restart când doar schimbi configurarea.
modul 01 / Linux esențial pentru servere
utilizatori, permisiuni & procese
de ce nu lucrezi ca root
root poate face orice, inclusiv distruge sistemul cu o greșeală de tastare. Practica standard: creezi un utilizator obișnuit și folosești sudo doar când ai nevoie de privilegii.
sudo adduser romeo # creez utilizator
sudo usermod -aG sudo romeo # îi dau drept de sudo
- sudo cere confirmare — te obligă să te oprești o secundă înainte de o comandă periculoasă.
- Lasă urmă în jurnale — știi cine ce a făcut.
- Limitează dauna unui proces compromis care rulează sub utilizatorul tău.
permisiuni & proprietate
ls -l
-rw-r--r-- 1 romeo www-data 1024 Jul 22 10:00 index.html
│└┬┘└┬┘└┬┘ └─┬─┘ └───┬──┘
│ │ │ └ alții │ └ grup
│ │ └──── grup └ proprietar
│ └─────── proprietar
└───────── tip (- fișier, d folder)
sudo chown -R romeo:www-data /var/www/site # schimb proprietarul
sudo chmod 755 /var/www/site # foldere
sudo find /var/www/site -type f -exec chmod 644 {} \;
Tiparul corect pentru un site: fișierele aparțin utilizatorului tău, grupul e cel al serverului web (www-data), iar serverul web are doar drept de citire — mai puțin pe folderele unde chiar trebuie să scrie (încărcări, cache).
principiul privilegiului minim
Fiecare proces primește strict permisiunile de care are nevoie, nimic în plus. Serverul web nu trebuie să poată scrie în folderul cu cod. Baza de date nu trebuie să ruleze ca root. Aplicația nu trebuie să aibă acces la fișiere care nu-i aparțin.
Motivul e practic: când (nu dacă) un serviciu e compromis, atacatorul moștenește exact permisiunile acelui proces. Cu privilegii minime, o breșă rămâne o problemă locală în loc să devină control total.
procese & resurse
htop # monitor interactiv (CPU, RAM, procese)
ps aux | grep nginx # caut un proces
kill -15 PID # opresc elegant (cere închidere)
kill -9 PID # forțez (ultimă soluție)
free -h # memorie
ss -tulpn # ce porturi sunt deschise și de cine
ss -tulpn e valoroasă la audit: îți arată exact ce ascultă pe ce port. Dacă vezi un serviciu expus pe care nu l-ai pus tu acolo, ai găsit o problemă.
sarcini programate
crontab -e
# minut oră zi lună zi-săptămână comandă
0 3 * * * /usr/local/bin/backup.sh # zilnic la 3:00
*/5 * * * * /usr/local/bin/verifica.sh # la 5 minute
Sfat practic: redirecționează ieșirea sarcinilor în jurnale. O sarcină programată care eșuează în tăcere e o problemă pe care o descoperi luni mai târziu — de obicei când ai nevoie de backup-ul pe care credeai că-l ai.
de reținut
Nu lucra ca root — folosește un utilizator obișnuit cu sudo: te obligă să te oprești o secundă și lasă urmă în jurnale. Aplică privilegiul minim: fiecare proces primește strict ce-i trebuie, pentru că o breșă moștenește exact acele permisiuni. Învață htop, ss -tulpn (ce ascultă pe ce port) și cron cu ieșirea trimisă în jurnale — sarcinile care eșuează în tăcere se descoperă prea târziu.
modul 02 / SSH & securizarea inițială
SSH cu chei: cum și de ce
SSH e modul prin care te conectezi la server. E și prima ușă pe care o încearcă atacatorii: un server nou, expus pe internet, primește mii de încercări de autentificare pe zi, automat, în primele ore.
parole vs chei
| Parolă | Cheie SSH |
| Poate fi ghicită | Da, prin forță brută | Practic imposibil |
| Se transmite | Da, la fiecare conectare | Nu — cheia privată nu părăsește calculatorul tău |
| Comoditate | O tastezi de fiecare dată | Automat |
O pereche de chei are două părți: cheia privată (rămâne la tine, secretă) și cheia publică (o pui pe server). Serverul îți dă o provocare criptografică pe care doar deținătorul cheii private o poate rezolva.
generarea & instalarea
# pe calculatorul TĂU
ssh-keygen -t ed25519 -C "romeo@laptop"
# → ~/.ssh/id_ed25519 (privată) și .pub (publică)
# copiez cheia publică pe server
ssh-copy-id romeo@IP_SERVER
# mă conectez
ssh romeo@IP_SERVER
- ed25519 e algoritmul recomandat: sigur, rapid, chei scurte.
- Pune o frază de acces pe cheia privată — dacă îți e furat laptopul, cheia rămâne inutilizabilă.
- Cheia privată nu se trimite nimănui, niciodată. Dacă un tutorial îți cere să încarci cheia privată undeva, e greșit sau rău intenționat.
securizarea configurării SSH
# /etc/ssh/sshd_config
PermitRootLogin no # root nu se poate conecta direct
PasswordAuthentication no # doar chei
PubkeyAuthentication yes
ordinea corectă — greșeala care te blochează afară
Dezactivarea parolelor înainte de a verifica autentificarea cu cheie e cea mai frecventă cale de a rămâne blocat în afara propriului server. Ordinea sigură:
- Adaugi cheia publică pe server.
- Deschizi o a doua sesiune SSH și confirmi că intri cu cheia, fără parolă.
- Păstrând prima sesiune deschisă, modifici configurarea și reîncarci serviciul.
- Testezi din a treia sesiune. Dacă ceva nu merge, prima sesiune încă e activă și poți repara.
Dacă totuși rămâi blocat, majoritatea furnizorilor oferă o consolă web de urgență — verifică din timp că știi unde e.
obiceiuri utile
# ~/.ssh/config pe calculatorul tău
Host server
HostName 203.0.113.45
User romeo
IdentityFile ~/.ssh/id_ed25519
# apoi doar: ssh server
Schimbarea portului SSH (de pe 22 pe altul) nu e securitate reală, dar reduce mult zgomotul din jurnale provenit de la scanările automate. E o măsură de igienă, nu de protecție.
de reținut
Folosește chei SSH (ed25519) în locul parolelor — cheia privată nu părăsește niciodată calculatorul tău și nu poate fi ghicită prin forță brută. Dezactivează autentificarea cu parolă și accesul direct al lui root. Cel mai important: respectă ordinea — verifică într-o a doua sesiune că intri cu cheia înainte de a dezactiva parolele, păstrând prima sesiune deschisă.
modul 02 / SSH & securizarea inițială
firewall, fail2ban & actualizări automate
firewall: închide tot, deschide ce trebuie
Principiul e simplu: refuză implicit tot traficul de intrare și deschizi doar porturile necesare.
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH # ÎNTÂI asta!
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw enable
sudo ufw status verbose
capcana clasică
Activarea firewall-ului fără a permite întâi SSH te deconectează instantaneu și definitiv. Regula: permite SSH ca primă comandă, verifică cu ufw status, abia apoi activează. Aceeași logică ca la SSH — verifică înainte de a închide ușa pe care stai.
ce NU expui niciodată
| Serviciu | Port | Regulă |
| MySQL / MariaDB | 3306 | Doar local sau rețea privată |
| PostgreSQL | 5432 | Doar local sau rețea privată |
| Redis | 6379 | Doar local — expus, e compromis în minute |
| Panouri de administrare | diverse | Prin SSH sau restricționate pe IP |
Bazele de date nu au ce căuta pe internetul public. Aplicația se conectează local; dacă e pe alt server, folosești rețeaua privată sau un tunel SSH.
fail2ban: blocarea atacurilor repetate
fail2ban citește jurnalele, detectează încercări repetate de autentificare eșuate și blochează temporar adresa IP respectivă în firewall.
sudo apt install fail2ban
# configurezi în /etc/fail2ban/jail.local
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd # cine e blocat
Cu chei SSH și parole dezactivate, riscul e deja mic — dar fail2ban reduce drastic zgomotul și protejează și alte servicii (formulare de autentificare, servere de mail).
actualizări automate de securitate
sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades
- Aplică automat doar corecțiile de securitate — nu actualizări majore care ar putea strica ceva.
- E cea mai bună raportare efort/beneficiu din tot modulul: cinci minute de configurare care te acoperă continuu.
- Configurează notificări și verifică periodic dacă serverul cere repornire după actualizări de nucleu.
lista de verificare pentru un server nou
- ✅ Sistem actualizat complet
- ✅ Utilizator obișnuit cu sudo (nu lucrezi ca root)
- ✅ Chei SSH funcționale, verificate în a doua sesiune
- ✅ Parole și acces root dezactivate în SSH
- ✅ Firewall activ, cu SSH permis înainte de activare
- ✅ fail2ban pornit
- ✅ Actualizări automate de securitate
- ✅ Fus orar corect, nume de gazdă setat
- ✅ Snapshot făcut acum, cât totul e curat
Această listă, parcursă în prima jumătate de oră, elimină categoria de atacuri care compromite majoritatea serverelor expuse.
de reținut
Firewall-ul refuză implicit tot și deschide doar ce e necesar — dar permite SSH înainte de a-l activa, altfel te deconectezi definitiv. Bazele de date nu se expun niciodată public (local sau rețea privată). Adaugă fail2ban împotriva atacurilor repetate și, cel mai important raport efort/beneficiu, actualizări automate de securitate. Parcurge lista de verificare în prima jumătate de oră și fă un snapshot cât serverul e curat.
modul 03 / Nginx: server web & reverse proxy
configurarea de bază & blocuri server
Nginx e serverul web pe care îl vei folosi cel mai des: rapid, eficient cu memoria, excelent la conexiuni multe simultan. Are două roluri distincte, pe care e important să le separi mental:
- Server web — servește direct fișiere statice (HTML, CSS, imagini).
- Reverse proxy — primește cererile și le trimite mai departe către aplicația ta (Node, PHP-FPM, Python), apoi întoarce răspunsul.
unde stau configurările
/etc/nginx/nginx.conf # configurarea globală
/etc/nginx/sites-available/ # configurări pentru fiecare site
/etc/nginx/sites-enabled/ # legături către cele active
Tiparul: scrii configurarea în sites-available, apoi creezi o legătură simbolică în sites-enabled ca să o activezi. Dezactivarea unui site înseamnă doar ștergerea legăturii — configurarea rămâne.
un bloc server pentru un site static
server {
listen 80;
server_name exemplu.ro www.exemplu.ro;
root /var/www/exemplu;
index index.html;
location / {
try_files $uri $uri/ =404;
}
access_log /var/log/nginx/exemplu.access.log;
error_log /var/log/nginx/exemplu.error.log;
}
| Directivă | Rol |
| listen | Pe ce port ascultă |
| server_name | Pentru ce domenii răspunde acest bloc |
| root | De unde servește fișierele |
| location | Reguli pentru anumite căi |
| try_files | Ce încearcă, în ordine, înainte de a da 404 |
fluxul obligatoriu de aplicare
sudo ln -s /etc/nginx/sites-available/exemplu /etc/nginx/sites-enabled/
sudo nginx -t # VERIFIC sintaxa
sudo systemctl reload nginx # aplic fără întrerupere
nginx -t înainte de orice reload
O configurare invalidă face ca Nginx să nu mai pornească deloc — și atunci cad toate site-urile de pe server, nu doar cel pe care l-ai modificat. nginx -t verifică sintaxa fără să aplice nimic și îți spune exact fișierul și linia cu problema. Fă-l reflex: editezi → nginx -t → reload. Niciodată reload direct.
ordinea de potrivire a blocurilor
Când sosește o cerere, Nginx alege blocul server după server_name. Dacă niciunul nu se potrivește, folosește blocul implicit — motiv pentru care e util să ai un bloc implicit care returnează 444 sau 404, ca serverul să nu răspundă cu un site aleatoriu la cereri către domenii necunoscute.
de reținut
Nginx are două roluri: server web (fișiere statice) și reverse proxy (trimite cererile către aplicația ta). Configurările stau în sites-available și se activează prin legături în sites-enabled. Un bloc server definește listen, server_name, root, location. Reflexul obligatoriu: nginx -t înainte de fiecare reload — o configurare invalidă oprește toate site-urile de pe server, nu doar cel modificat.
modul 03 / Nginx: server web & reverse proxy
reverse proxy, TLS & optimizări
reverse proxy — modelul standard pentru aplicații
Aplicația ta (Node, Python, Go) ascultă local, pe un port intern. Nginx stă în față, primește traficul public și îl transmite mai departe:
server {
listen 80;
server_name app.exemplu.ro;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
de ce nu expui aplicația direct
- TLS într-un singur loc — certificatul se configurează la Nginx, nu în fiecare aplicație.
- Fișiere statice servite eficient — Nginx e mult mai bun la asta decât aplicația ta.
- Mai multe aplicații pe un server, pe același port 443, diferențiate prin domeniu.
- Limitare de rată, caching, compresie, antete — configurate central.
- Aplicația nu e expusă direct — ascultă doar pe 127.0.0.1.
antetele de proxy nu sunt opționale
Fără X-Real-IP și X-Forwarded-For, aplicația ta va crede că toate cererile vin de la 127.0.0.1 — adică de la Nginx. Consecințele sunt reale: jurnalele devin inutile, limitarea de rată per utilizator nu funcționează, geolocalizarea eșuează, iar detectarea abuzurilor devine imposibilă. Iar fără X-Forwarded-Proto, aplicația nu știe că cererea a venit pe HTTPS și poate genera linkuri http, provocând redirecționări în buclă.
TLS cu Let's Encrypt
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d exemplu.ro -d www.exemplu.ro
# reînnoirea e automată; verific că funcționează:
sudo certbot renew --dry-run
Certbot modifică singur configurarea Nginx: adaugă blocul pentru portul 443, căile către certificate și redirecționarea de pe HTTP. Reînnoirea se face automat prin sarcină programată — dar verifică periodic că funcționează (modulul 9, monitorizare).
optimizări utile
# compresie
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml;
# cache pentru fișiere statice
location ~* \.(jpg|jpeg|png|webp|css|js|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# antete de securitate
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
# limitarea mărimii încărcărilor
client_max_body_size 20M;
client_max_body_size merită reținut: valoarea implicită e mică (1 MB), iar simptomul depășirii e o eroare 413 la încărcarea fișierelor — o problemă frecventă și derutantă la instalarea unui CMS.
de reținut
Modelul standard: aplicația ascultă local, Nginx e reverse proxy în față — TLS într-un singur loc, fișiere statice servite eficient, mai multe aplicații pe același server. Antetele de proxy sunt obligatorii: fără ele, aplicația crede că tot traficul vine de la 127.0.0.1 și pierzi jurnale, limitare de rată și detectarea abuzurilor. Certificatele se obțin cu certbot, iar reînnoirea automată trebuie verificată periodic.
modul 04 / rularea aplicațiilor în producție
systemd: aplicația care nu moare
Dacă pornești aplicația cu node server.js în terminal, ea moare când închizi sesiunea SSH. În producție ai nevoie de un proces care pornește la boot și repornește automat la eșec. Pe Linux modern, acesta e systemd.
un serviciu systemd
# /etc/systemd/system/aplicatie.service
[Unit]
Description=Aplicația mea Node
After=network.target
[Service]
Type=simple
User=aplicatie
WorkingDirectory=/var/www/aplicatie
ExecStart=/usr/bin/node server.js
Restart=always
RestartSec=5
Environment=NODE_ENV=production
EnvironmentFile=/etc/aplicatie/mediu
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload # reciteşte fișierele de serviciu
sudo systemctl enable --now aplicatie
sudo systemctl status aplicatie
sudo journalctl -u aplicatie -f # jurnalele, în timp real
directivele care contează
| Directivă | De ce contează |
| User | Rulează sub un utilizator dedicat, niciodată root |
| Restart=always | Repornește automat dacă procesul cade |
| RestartSec | Pauză între încercări — previne bucla de repornire rapidă |
| EnvironmentFile | Secretele stau într-un fișier cu permisiuni restrânse, nu în serviciu |
| After | Ordinea de pornire (ex: după rețea sau după baza de date) |
utilizator dedicat per aplicație
Fiecare aplicație ar trebui să ruleze sub propriul utilizator de sistem, fără shell de autentificare și fără drepturi în plus. Motivul e cel din modulul 1: dacă aplicația e compromisă, atacatorul moștenește exact permisiunile ei. O aplicație care rulează ca root transformă orice vulnerabilitate minoră în control total asupra serverului.
jurnalele
Cu systemd, ieșirea aplicației ajunge automat în jurnalul de sistem:
journalctl -u aplicatie -f # urmăresc în timp real
journalctl -u aplicatie --since "1 hour ago"
journalctl -u aplicatie -p err # doar erori
Nu mai ai nevoie să redirecționezi manual ieșirea în fișiere — systemd gestionează rotația și accesul.
de reținut
În producție, aplicațiile rulează ca servicii systemd: pornesc la boot, repornesc automat la eșec (Restart=always) și își trimit jurnalele în journalctl. Regula esențială: fiecare aplicație sub propriul utilizator dedicat, niciodată root — altfel o vulnerabilitate minoră devine control total. Secretele stau într-un EnvironmentFile cu permisiuni restrânse.
modul 04 / rularea aplicațiilor în producție
PHP-FPM, variabile de mediu & publicare
PHP-FPM: cum rulează PHP modern
PHP nu mai rulează ca modul în serverul web. PHP-FPM e un proces separat care execută codul PHP; Nginx îi trimite cererile printr-un socket.
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
Fiecare aplicație poate avea propriul pool PHP-FPM, cu utilizator și limite proprii — izolare între site-uri pe același server.
| Setare pool | Ce controlează |
| pm.max_children | Câte procese simultan — limitarea principală de memorie |
| user / group | Sub ce utilizator rulează codul |
| memory_limit | Cât poate consuma un singur script |
calculul care previne căderea serverului
pm.max_children setat prea sus e o cauză clasică de server care „moare" sub trafic: procesele consumă toată memoria, sistemul începe să folosească swap sau omoară procese, și totul se blochează.
Calcul simplu: (RAM disponibil pentru PHP) ÷ (memoria medie a unui proces). Dacă ai 2 GB pentru PHP și un proces consumă ~50 MB, max_children ≈ 40 — nu 200, oricât de tentant ar părea.
variabile de mediu & secrete
- Niciodată în cod și niciodată în repository — nici măcar „temporar".
- Într-un fișier cu permisiuni 600, deținut de utilizatorul aplicației.
- Încărcate prin EnvironmentFile (systemd) sau prin mecanismul aplicației.
- Valori diferite pentru test și producție.
- Dacă un secret s-a scurs vreodată într-un commit, consideră-l compromis și rotește-l — istoricul Git rămâne accesibil chiar dacă ștergi fișierul ulterior.
publicarea fără întreruperi
Dacă ștergi fișierele vechi și copiezi altele noi, site-ul e stricat câteva secunde. Tiparul profesionist folosește versiuni și o legătură simbolică:
/var/www/aplicatie/
versiuni/
2026-07-22-1030/ # versiunea anterioară
2026-07-22-1415/ # versiunea nouă
partajat/ # încărcări, fișiere de mediu
curent -> versiuni/2026-07-22-1415
- Pregătești noua versiune complet, într-un folder nou (cod, dependențe, compilare).
- Muți legătura curent spre ea — operație instantanee.
- Reîncarci aplicația (reload, nu restart).
- Dacă ceva nu merge, muți legătura înapoi — revenire în câteva secunde.
Datele care trebuie să supraviețuiască publicărilor (încărcări, fișiere de mediu) stau în partajat/ și sunt legate în fiecare versiune.
de reținut
PHP-FPM rulează separat de Nginx, cu pool-uri izolate per aplicație — iar pm.max_children calculat greșit e o cauză clasică de server căzut sub trafic. Secretele stau în fișiere cu permisiuni 600, niciodată în repository; unul scurs într-un commit e compromis definitiv. Pentru publicare, folosește versiuni + legătură simbolică: schimbarea e instantanee, iar revenirea la versiunea anterioară durează secunde.
modul 05 / baze de date pe server propriu
instalare, securizare & utilizatori
Pe un server propriu, baza de date e responsabilitatea ta completă: instalare, securizare, backup, performanță. Vom folosi MySQL/MariaDB ca exemplu, dar principiile sunt aceleași pentru PostgreSQL.
instalare & securizare inițială
sudo apt install mariadb-server
sudo mysql_secure_installation
Scriptul de securizare face lucruri esențiale: elimină utilizatorii anonimi, interzice conectarea root de la distanță, șterge baza de date de test. Rulează-l întotdeauna imediat după instalare.
regula fundamentală: nu expune baza de date
# /etc/mysql/mariadb.conf.d/50-server.cnf
bind-address = 127.0.0.1
bazele de date expuse public
O bază de date accesibilă de pe internet e scanată și atacată în minute. Există campanii automate care caută MySQL, PostgreSQL, Redis și MongoDB expuse, se conectează cu credențiale implicite sau slabe, exfiltrează datele și adesea le șterg, lăsând o cerere de răscumpărare.
Regula: bind-address = 127.0.0.1, firewall care blochează portul, iar dacă aplicația e pe alt server — rețea privată sau tunel SSH, niciodată internet public.
utilizatori & privilegii
CREATE DATABASE aplicatie CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
CREATE USER 'aplicatie'@'localhost'
IDENTIFIED BY 'parolă-lungă-și-unică';
GRANT SELECT, INSERT, UPDATE, DELETE
ON aplicatie.* TO 'aplicatie'@'localhost';
FLUSH PRIVILEGES;
- Un utilizator per aplicație, limitat la baza lui.
- Doar privilegiile necesare — o aplicație obișnuită nu are nevoie de DROP sau GRANT.
- utf8mb4 obligatoriu — suportă corect diacriticele românești și emoji; vechiul „utf8" din MySQL e incomplet și produce caractere stricate.
- Utilizatorul de migrări (care schimbă structura) poate fi separat de cel folosit de aplicație în rulare.
backup automat
#!/bin/bash
# /usr/local/bin/backup-db.sh
DATA=$(date +%F-%H%M)
mysqldump --single-transaction --routines aplicatie \
| gzip > /var/backups/db/aplicatie-$DATA.sql.gz
# păstrez ultimele 14 zile
find /var/backups/db -name "*.sql.gz" -mtime +14 -delete
# copiez în afara serverului
rclone copy /var/backups/db stocare-externa:backups/db
- --single-transaction — copie consistentă fără a bloca tabelele.
- Copia în afara serverului e obligatorie — un backup care stă doar pe serverul pe care îl protejezi nu te ajută când serverul dispare.
- Programezi prin cron și trimiți ieșirea în jurnale.
- Testează restaurarea — un backup netestat e o speranță (modulul 10).
de reținut
După instalare, rulează scriptul de securizare și setează bind-address = 127.0.0.1 — o bază de date expusă public e atacată în minute de campanii automate. Creează un utilizator per aplicație, cu privilegii minime, și folosește utf8mb4 pentru diacritice corecte. Backup-ul se face automat, cu --single-transaction, se rotește și, obligatoriu, se copiază în afara serverului.
modul 05 / baze de date pe server propriu
performanță & întreținere
de unde vine lentoarea
În aproape toate cazurile, o bază de date lentă înseamnă una dintre următoarele — în această ordine de frecvență:
- Lipsa indecșilor — interogarea parcurge tot tabelul în loc să sară direct la rânduri.
- Interogări în buclă (problema N+1) — 200 de interogări mici în loc de una.
- Memorie insuficientă pentru cache — datele se citesc de pe disc constant.
- Interogări care aduc prea mult — SELECT * pe tabele mari.
indecșii — cea mai mare diferență, cu cel mai mic efort
Un index face pentru baza de date ce face cuprinsul pentru o carte: în loc să citească toate paginile, sare direct unde trebuie. Pe un tabel de un milion de rânduri, adăugarea unui index pe coloana după care filtrezi poate transforma o interogare de 2 secunde în 2 milisecunde.
Indexezi coloanele folosite în WHERE, JOIN și ORDER BY. Dar nu la întâmplare: fiecare index încetinește scrierile și ocupă spațiu. Indexează după măsurători, nu după intuiție.
găsirea interogărilor lente
# activez jurnalul de interogări lente
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1 # peste 1 secundă
EXPLAIN SELECT * FROM comenzi WHERE email = 'x@y.ro';
EXPLAIN îți arată cum execută baza interogarea. Dacă vezi că parcurge un număr mare de rânduri pentru a returna câteva, îți lipsește un index.
reglaje de bază
| Parametru | Rol | Orientativ |
| innodb_buffer_pool_size | Memoria pentru date și indecși — cel mai important parametru | 50–70% din RAM pe un server dedicat bazei |
| max_connections | Conexiuni simultane | Cât are nevoie aplicația, nu mai mult |
| innodb_flush_log_at_trx_commit | Echilibru între siguranță și viteză la scriere | 1 pentru date critice |
Atenție la suma memoriei: dacă serverul rulează și aplicația, și baza, și Nginx, iar tu aloci 70% din RAM bazei, sistemul va rămâne fără memorie. Calculează totalul, nu fiecare serviciu izolat.
întreținere periodică
- Verifică dimensiunea bazei și creșterea ei — o creștere bruscă indică o problemă (jurnale în tabele, sesiuni neșterse).
- Curăță datele efemere — sesiuni expirate, jurnale vechi, cozi procesate.
- Monitorizează conexiunile — dacă se apropie de limită, aplicația probabil nu le eliberează corect.
- Actualizează versiunea majoră planificat, cu backup și test prealabil.
de reținut
Lentoarea vine, în ordine, din: lipsa indecșilor, interogări în buclă (N+1), memorie insuficientă și interogări care aduc prea mult. Indecșii dau cea mai mare îmbunătățire cu cel mai mic efort — dar se adaugă după măsurători (jurnal de interogări lente + EXPLAIN), pentru că fiecare index încetinește scrierile. Iar la reglaje, calculează suma memoriei tuturor serviciilor, nu fiecare izolat.
modul 06 / Docker: containere
de ce containere & noțiunile de bază
Docker rezolvă o problemă veche și foarte concretă: „la mine funcționează". Un container împachetează aplicația împreună cu tot ce-i trebuie — versiunea exactă de limbaj, bibliotecile, configurarea — astfel încât rulează identic oriunde.
problema fără containere
- Serverul are PHP 8.1, tu dezvolți pe 8.3 — apar diferențe subtile de comportament.
- Două aplicații pe același server cer versiuni incompatibile ale aceleiași biblioteci.
- Un coleg nou pierde o zi ca să-și configureze mediul local.
- Actualizarea unei biblioteci de sistem strică pe neașteptate o aplicație care mergea.
container vs mașină virtuală
| Mașină virtuală | Container |
| Conține | Sistem de operare complet | Doar aplicația și dependențele ei |
| Mărime | Gigabytes | Zeci–sute de megabytes |
| Pornire | Zeci de secunde | Milisecunde–secunde |
| Izolare | Completă (nucleu propriu) | La nivel de proces (nucleu partajat) |
Containerele împart nucleul sistemului gazdă — de aici vin viteza și consumul mic. Izolarea e reală, dar mai puțin strictă decât la mașini virtuale.
imagine vs container
IMAGINE = șablonul, doar-citire (rețeta)
CONTAINER = o instanță care rulează din acea imagine (felul gătit)
Dintr-o imagine poți porni oricâte containere. Imaginile se construiesc în straturi: fiecare instrucțiune adaugă un strat, iar straturile nemodificate sunt refolosite din cache — de aceea o reconstrucție e adesea aproape instantanee.
comenzile de bază
docker run -d --name web -p 8080:80 nginx # pornesc
docker ps # ce rulează
docker logs -f web # jurnalele
docker exec -it web bash # intru în container
docker stop web && docker rm web # opresc și șterg
docker images # imaginile locale
docker system prune -a # eliberez spațiu
containerele sunt efemere — datele nu stau în ele
Un container șters pierde tot ce s-a scris în el. Asta e intenționat: containerele trebuie tratate ca de unică folosință, înlocuibile oricând.
Datele care trebuie păstrate — baza de date, fișierele încărcate de utilizatori — stau în volume, în afara containerului. Aceasta e cea mai importantă distincție de înțeles la început: containerul e procesul, volumul e memoria.
de reținut
Containerele împachetează aplicația cu tot ce-i trebuie, deci rulează identic oriunde — rezolvarea directă a lui „la mine funcționează". Sunt mult mai ușoare decât mașinile virtuale pentru că împart nucleul gazdei. Reține distincția imagine (șablon) vs container (instanță care rulează) și, cel mai important: containerele sunt efemere — datele care trebuie păstrate stau în volume.
modul 06 / Docker: containere
Dockerfile, volume & rețele
Dockerfile: rețeta imaginii
FROM node:22-alpine
WORKDIR /app
# întâi dependențele — strat cacheat separat
COPY package*.json ./
RUN npm ci --omit=dev
# apoi codul, care se schimbă des
COPY . .
USER node
EXPOSE 3000
CMD ["node", "server.js"]
ordinea instrucțiunilor decide viteza construcției
Docker cachează fiecare strat. Dacă un strat se schimbă, toate cele de după el se reconstruiesc.
De aceea copiezi întâi fișierele de dependențe și instalezi, și abia apoi codul: la o modificare de cod, instalarea dependențelor (partea lentă) se ia din cache. Ordinea inversă — COPY . . înainte de npm ci — reinstalează totul la fiecare mică schimbare și transformă o construcție de 5 secunde în una de 3 minute.
bune practici pentru imagini
- Imagini de bază mici — variantele „alpine" sau „slim" reduc mult dimensiunea și suprafața de atac.
- Versiuni fixate — node:22-alpine, nu node:latest, care se schimbă sub tine.
- Construcție în mai multe etape — compilezi într-o etapă, copiezi doar rezultatul în imaginea finală.
- USER — nu rula ca root în container.
- .dockerignore — exclude node_modules, .git, fișiere de mediu. Fără el, construcția e lentă și poți include secrete în imagine.
- Niciodată secrete în Dockerfile — rămân în straturile imaginii, vizibile oricui o are.
volume: datele care supraviețuiesc
# volum gestionat de Docker (recomandat pentru baze de date)
docker run -v date-mysql:/var/lib/mysql mariadb
# legătură către un folder de pe gazdă (util în dezvoltare)
docker run -v /var/www/site:/app node
| Tip | Când îl folosești |
| Volum gestionat | Date de producție (baze de date) — Docker se ocupă de stocare |
| Legătură la folder | Dezvoltare: editezi local, vezi imediat în container |
rețele
docker network create app-net
docker run -d --name db --network app-net mariadb
docker run -d --name api --network app-net imaginea-mea
Containerele din aceeași rețea se găsesc după nume: aplicația se conectează la db:3306, fără să știe adrese IP. Iar dacă nu publici portul bazei de date către exterior (fără -p), ea e accesibilă doar din rețeaua internă — exact izolarea pe care o vrei.
de reținut
În Dockerfile, ordinea instrucțiunilor decide viteza: dependențele înainte de cod, ca stratul lent să vină din cache. Folosește imagini mici și cu versiune fixată, un .dockerignore, un utilizator ne-root, și niciodată secrete (rămân în straturi). Volumele păstrează datele dincolo de viața containerului, iar rețelele permit containerelor să se găsească după nume, fără a expune porturi public.
modul 07 / Docker Compose & aplicații multi-serviciu
Compose: stiva într-un fișier
O aplicație reală are mai multe piese: server web, aplicație, bază de date, poate cache. Pornirea lor manuală, în ordine, cu rețele și volume, devine repede obositoare. Docker Compose descrie toată stiva într-un singur fișier.
un exemplu complet
services:
app:
build: .
restart: unless-stopped
env_file: .env
depends_on:
db:
condition: service_healthy
ports:
- "127.0.0.1:3000:3000"
db:
image: mariadb:11
restart: unless-stopped
environment:
MARIADB_DATABASE: aplicatie
MARIADB_ROOT_PASSWORD: ${'{'}DB_ROOT{'}'}
volumes:
- date-db:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect"]
interval: 10s
retries: 5
volumes:
date-db:
docker compose up -d # pornesc tot
docker compose ps # starea serviciilor
docker compose logs -f app # jurnalele unui serviciu
docker compose down # opresc (volumele rămân)
docker compose down -v # opresc ȘI ȘTERG volumele (atenție!)
detaliile care contează
| Element | De ce e important |
| 127.0.0.1:3000:3000 | Publică portul doar local — Nginx îl accesează, internetul nu |
| restart: unless-stopped | Repornire automată după cădere sau repornirea serverului |
| healthcheck | Docker știe dacă serviciul e cu adevărat funcțional, nu doar pornit |
| condition: service_healthy | Aplicația pornește abia când baza chiar acceptă conexiuni |
| volumes | Datele supraviețuiesc recreării containerelor |
două capcane clasice
1. Publicarea porturilor fără adresă. ports: "3306:3306" expune baza de date pe internet, ocolind firewall-ul gazdei — Docker își scrie propriile reguli. Folosește "127.0.0.1:3306:3306" sau, mai bine, nu publica deloc portul: containerele din aceeași rețea se văd oricum.
2. depends_on fără healthcheck așteaptă doar ca containerul să pornească, nu ca serviciul să fie gata. Aplicația încearcă să se conecteze la o bază care încă se inițializează și cade. De aceea combini depends_on cu condition: service_healthy.
de reținut
Docker Compose descrie toată stiva într-un fișier: servicii, volume, rețele, dependențe. Reține două detalii care fac diferența în producție: publică porturile legate la 127.0.0.1 (altfel Docker ocolește firewall-ul și expui baza de date public) și combină depends_on cu healthcheck, altfel aplicația pornește înainte ca baza să fie gata. Iar down -v șterge volumele — folosește-l cu atenție.
modul 07 / Docker Compose & aplicații multi-serviciu
reverse proxy, medii & mentenanță
Nginx în fața containerelor
Tiparul cel mai răspândit pentru un server cu mai multe aplicații containerizate: Nginx pe gazdă, containerele ascultă doar local.
# pe gazdă: /etc/nginx/sites-available/app
server {
server_name app.exemplu.ro;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Avantajul: certificatele TLS și configurarea rămân pe gazdă, gestionate cu certbot ca de obicei, iar containerele nu trebuie să știe nimic despre HTTPS. Alternativa e un proxy rulat tot în container, cu obținerea automată a certificatelor — util când ai multe aplicații care apar și dispar.
medii separate
docker-compose.yml # baza, comună
docker-compose.override.yml # dezvoltare (aplicat automat)
docker-compose.prod.yml # producție
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
- În dezvoltare: legături la foldere locale (vezi modificările instant), porturi expuse pentru depanare, jurnale detaliate.
- În producție: imagini construite, fără legături la cod, resurse limitate, repornire automată.
- Fișierul de mediu nu se comite niciodată — doar un exemplu, fără valori reale.
mentenanță
docker compose pull # aduc imagini actualizate
docker compose up -d --build # reconstruiesc și repornesc
docker system prune -a # eliberez spațiu
docker stats # consum în timp real
discul plin din cauza Docker
O cauză frecventă și derutantă de server blocat: imaginile vechi, containerele oprite și jurnalele containerelor umplu discul. Simptomele sunt bizare (aplicația nu mai scrie, baza dă erori ciudate), iar cauza nu e evidentă.
Prevenire: rulează periodic docker system prune, limitează dimensiunea jurnalelor în configurarea Docker și monitorizează spațiul liber cu alertă la 80%.
limitarea resurselor
services:
app:
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
Fără limite, un container cu o scurgere de memorie poate consuma tot RAM-ul și poate face inutilizabil întregul server, inclusiv celelalte aplicații. Limitele transformă o problemă locală într-una izolată.
de reținut
Tiparul standard: Nginx pe gazdă, containerele ascultă local — TLS rămâne gestionat cu certbot ca de obicei. Ține medii separate (dezvoltare cu legături la cod, producție cu imagini construite) și nu comite niciodată fișierul de mediu. Două lucruri de întreținut activ: curăță periodic Docker (imaginile și jurnalele umplu discul, cu simptome derutante) și limitează resursele per container, ca o scurgere de memorie să nu doboare tot serverul.
modul 08 / CI/CD: publicare automată
pipeline & publicare automată
CI/CD înseamnă: la fiecare modificare de cod, un sistem automat verifică, construiește și publică. Elimină cea mai frecventă sursă de incidente în producție — publicarea manuală făcută grăbit.
ce rezolvă
| Publicare manuală | Publicare automată |
| Pași uitați sau făcuți în altă ordine | Aceeași secvență, de fiecare dată |
| „Merge la mine" ajunge în producție | Testele rulează înainte, obligatoriu |
| Doar o persoană știe procedura | Procedura e cod, citibilă de toți |
| Frică de a publica → publicări rare și mari | Publicări mici și dese, cu risc mic |
paradoxul publicărilor
Intuiția spune: „publicăm rar, ca să reducem riscul". În practică se întâmplă invers. O publicare rară adună zeci de modificări; când ceva se strică, nu știi care dintre ele e vinovată, iar revenirea anulează și lucrurile bune.
Publicările mici și dese sunt mai sigure: fiecare conține puține modificări, cauza unei probleme e evidentă, iar revenirea e ieftină. Automatizarea e ceea ce face posibile publicările dese.
etapele unui pipeline
push în Git
↓
[1] Verificări — stil de cod, analiză statică
↓
[2] Teste — unitare, integrare
↓
[3] Construcție — imagine Docker / pachet
↓
[4] Publicare test — mediu de verificare
↓
[5] Publicare prod — automat sau cu aprobare
un pipeline concret
name: Publicare
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test
publica:
needs: test # publică DOAR dacă testele trec
runs-on: ubuntu-latest
steps:
- uses: appleboy/ssh-action@v1
with:
host: ${'{{'} secrets.HOST {'}}'}
key: ${'{{'} secrets.SSH_KEY {'}}'}
script: /usr/local/bin/publica.sh
needs: test e esențial: publicarea rulează doar dacă testele au trecut. Fără el, ai automatizat publicarea codului stricat.
de reținut
CI/CD transformă publicarea într-o procedură repetabilă, verificată automat — și elimină publicarea manuală grăbită, cea mai frecventă sursă de incidente. Contraintuitiv dar esențial: publicările mici și dese sunt mai sigure decât cele rare și mari, pentru că izolezi cauza și revii ieftin. Iar în pipeline, publicarea trebuie condiționată de trecerea testelor.
modul 08 / CI/CD: publicare automată
strategii de publicare, revenire & secrete
scriptul de publicare
#!/bin/bash
set -euo pipefail # oprește la prima eroare
cd /var/www/aplicatie
VERSIUNE=$(date +%F-%H%M%S)
git clone --depth 1 repo versiuni/$VERSIUNE
cd versiuni/$VERSIUNE
npm ci --omit=dev
npm run build
ln -s /var/www/aplicatie/partajat/.env .env
ln -sfn /var/www/aplicatie/versiuni/$VERSIUNE /var/www/aplicatie/curent
sudo systemctl reload aplicatie
# păstrez ultimele 5 versiuni
ls -dt /var/www/aplicatie/versiuni/* | tail -n +6 | xargs rm -rf
set -euo pipefail — o linie care previne dezastre
Fără ea, un script bash continuă după o eroare. Dacă npm run build eșuează dar scriptul merge mai departe, muți legătura către o versiune incompletă și publici un site stricat — automat, cu încredere.
set -e oprește la prima eroare, -u la variabile nedefinite, -o pipefail prinde erorile din mijlocul unui lanț de comenzi. Prima linie a oricărui script de publicare.
strategii de publicare
| Strategie | Cum funcționează |
| Recreare | Oprești vechiul, pornești noul — simplu, dar cu întrerupere |
| Rulare paralelă | Pornești noua versiune lângă cea veche, apoi comuți traficul — fără întrerupere |
| Progresivă | Trimiți la început un procent mic de trafic spre noua versiune, apoi crești |
Pentru majoritatea proiectelor mici și medii, tiparul cu versiuni și legătură simbolică (modulul 4) oferă practic zero întrerupere, cu complexitate minimă. Nu ai nevoie de orchestrare complicată ca să publici bine.
revenirea — planul pe care trebuie să-l ai înainte
# revenire la versiunea anterioară
ANTERIOARA=$(ls -dt /var/www/aplicatie/versiuni/* | sed -n 2p)
ln -sfn $ANTERIOARA /var/www/aplicatie/curent
sudo systemctl reload aplicatie
- Revenirea trebuie să fie mai rapidă decât diagnosticarea. Când producția e stricată, întâi restabilești serviciul, apoi investighezi în liniște.
- Testeaz-o înainte — o procedură de revenire netestată e la fel de nesigură ca un backup netestat.
- Atenție la migrările de bază de date: codul revine ușor, structura bazei nu. Preferă modificări compatibile cu ambele versiuni (adaugi coloane, nu le ștergi imediat).
secrete în pipeline
- Stocate în secretele platformei de CI, nu în fișierul de configurare.
- Chei SSH dedicate pentru publicare, cu drepturi limitate — nu cheia ta personală.
- Rotire periodică și revocare imediată la plecarea cuiva din echipă.
- Atenție ce afișezi în jurnalele pipeline-ului — un echo nefericit poate publica un secret în istoricul de rulări.
de reținut
Începe orice script de publicare cu set -euo pipefail — altfel o eroare la construcție duce la publicarea automată a unei versiuni incomplete. Pentru proiecte mici și medii, versiuni + legătură simbolică dau practic zero întrerupere fără complexitate. Cel mai important: pregătește și testează procedura de revenire înainte de a avea nevoie de ea — și ai grijă la migrările de bază de date, care nu se anulează la fel de ușor ca codul.
modul 09 / monitorizare, jurnale & scalare
monitorizare & jurnale
Un server pe care nu îl monitorizezi îți spune că are o problemă prin gura clienților. Monitorizarea înseamnă să afli tu primul, ideal înainte ca utilizatorii să observe.
ce monitorizezi, pe niveluri
| Nivel | Indicatori | Prag tipic de alertă |
| Disponibilitate | Răspunde site-ul? Cod 200? | Orice eșec |
| Resurse | CPU, RAM, disc, încărcare | Disc >80%, RAM >90% |
| Aplicație | Timp de răspuns, rata de erori | Erori 5xx în creștere |
| Bază de date | Conexiuni, interogări lente | Conexiuni aproape de limită |
| Certificat & domeniu | Zile până la expirare | Sub 14 zile |
| Backup | A rulat? Ce dimensiune are? | Lipsă sau dimensiune anormală |
discul plin — cea mai frecventă cădere evitabilă
Un disc umplut e probabil cauza numărul unu de servere care „se strică singure". Simptomele sunt derutante: baza de date refuză scrieri, sesiunile se pierd, aplicația dă erori aparent aleatorii, iar cauza reală nu e evidentă din mesaje.
Sursele obișnuite: jurnale care cresc necontrolat, imagini Docker vechi, backup-uri locale neșterse, fișiere temporare. O singură alertă la 80% ocupare îți economisește ore de depanare — și e cea mai bună alertă pe care o poți configura.
jurnalele: unde cauți
journalctl -u nginx -f # serviciu, timp real
journalctl -p err --since today # doar erori, azi
tail -f /var/log/nginx/error.log # jurnalul Nginx
docker compose logs -f app # container
dmesg -T | tail # mesaje de nucleu (ex: procese oprite din lipsă de memorie)
dmesg merită reținut: dacă un proces dispare fără explicație, adesea a fost oprit de sistem din lipsă de memorie — și scrie acolo, nu în jurnalul aplicației.
rotația jurnalelor
Jurnalele cresc la nesfârșit dacă nu le rotești. logrotate le arhivează, comprimă și șterge pe cele vechi:
/var/log/aplicatie/*.log {
daily
rotate 14
compress
missingok
notifempty
}
Pentru containere, limitezi dimensiunea jurnalelor în configurarea Docker — altfel un container vorbăreț poate umple discul singur.
alerte care nu se ignoră
- Puține și relevante — dacă primești 50 de alerte pe zi, le vei ignora pe toate, inclusiv pe cea importantă.
- Acționabile — fiecare alertă trebuie să-ți spună ce să faci, nu doar că ceva e „ciudat".
- Praguri realiste, ajustate după observarea comportamentului normal.
- Canale diferite — email pentru avertismente, notificare instantă pentru urgențe.
de reținut
Monitorizează pe niveluri: disponibilitate, resurse, erori de aplicație, bază de date, certificate, backup. Cea mai valoroasă alertă e cea pentru disc >80% — discul plin e cauza numărul unu de căderi evitabile, cu simptome derutante. Rotește jurnalele (logrotate, limite pentru containere) și ține alertele puține și acționabile: 50 de alerte pe zi înseamnă zero alerte citite.
modul 09 / monitorizare, jurnale & scalare
performanță & scalare
întâi măsori, apoi optimizezi
Optimizarea fără măsurare e ghicit. Ordinea corectă: măsori → identifici gâtuirea → optimizezi acolo → măsori din nou. Altfel petreci ore reglând ceva care nu era problema.
| Simptom | Verifici |
| CPU constant la maxim | Cod ineficient, lipsa cache-ului, procese fugare |
| RAM plin, sistem în swap | Prea multe procese (max_children), scurgeri de memorie, limite lipsă |
| Disc solicitat constant | Interogări fără indecși, jurnale excesive, lipsa cache-ului |
| Totul pare liber, dar e lent | Aștepți după un serviciu extern (API, DNS, plăți) |
ultimul caz e cel mai des ratat
Server relaxat, dar răspunsuri lente — aproape întotdeauna înseamnă că aplicația așteaptă după altcineva: un API extern fără timeout, o rezolvare DNS lentă, un serviciu de plăți care răspunde greu.
Remedii: pune timeout pe orice apel extern (fără el, un serviciu blocat îți blochează aplicația), mută munca lentă în cozi asincrone și cachează răspunsurile externe care nu se schimbă des.
ordinea optimizărilor, după raport efort/beneficiu
- Caching — pagini, obiecte, interogări repetate. Cel mai mare câștig, cel mai mic efort.
- Indecși în baza de date (modulul 5).
- Compresie și cache pentru fișiere statice în Nginx.
- CDN pentru resursele statice.
- Cod — eliminarea interogărilor în buclă, procesare asincronă.
- Resurse mai mari — abia la final, după ce ai epuizat restul.
Mărirea serverului e adesea prima reacție și ultima soluție corectă: dublezi costul și ascunzi problema, care revine peste câteva luni la o scară mai mare.
scalare verticală vs orizontală
| Verticală | Orizontală |
| Ce faci | Server mai mare | Mai multe servere |
| Complexitate | Minimă | Mare (echilibrare, stare partajată) |
| Limită | Există un maxim | Practic nelimitată |
| Rezistență la defecte | Un singur punct de eșec | Serverele se pot înlocui |
Pentru majoritatea proiectelor, scalarea verticală ajunge mult mai departe decât se crede. Un singur server bine reglat poate servi zeci de mii de vizitatori zilnici. Treci la orizontal când ai nevoie de redundanță (să nu cadă tot dacă pică un server) sau chiar depășești limitele unei mașini.
ce trebuie pregătit pentru scalare orizontală
- Aplicație fără stare locală — sesiunile și fișierele nu pot sta pe disc local; merg în stocare partajată sau Redis.
- Bază de date separată de serverele de aplicație.
- Echilibrator de sarcină în față.
- Publicare automată — actualizarea manuală a cinci servere nu e sustenabilă.
Observă că ultimele două module (containere, CI/CD) sunt exact pregătirea pentru asta: dacă aplicația e containerizată și publicarea e automată, adăugarea unui server devine o operație de rutină în loc de un proiect.
de reținut
Măsoară înainte de a optimiza — altfel reglezi ce nu era problema. Cazul cel mai des ratat: server relaxat dar lent înseamnă că aștepți după un serviciu extern; pune timeout pe orice apel extern. Ordinea corectă a optimizărilor începe cu caching și indecși și se termină cu resurse mai mari, nu invers. Iar scalarea verticală ajunge mult mai departe decât se crede — treci la orizontal pentru redundanță, nu doar pentru viteză.
modul 10 / securitate, recuperare · proiect & test final
întărire, backup & răspuns la incidente
lista de întărire a unui server
| Măsură | De ce |
| Chei SSH, fără parole, fără root | Elimină atacurile prin forță brută |
| Firewall restrictiv | Doar porturile necesare; bazele de date niciodată publice |
| Actualizări automate de securitate | Cel mai bun raport efort/beneficiu |
| fail2ban | Blochează încercările repetate |
| Utilizator dedicat per serviciu | Privilegiu minim — limitează dauna unei breșe |
| Secrete în fișiere 600 | Niciodată în cod sau repository |
| Servicii inutile oprite | Fiecare serviciu deschis e o suprafață de atac |
| Antete de securitate în Nginx | Protecție la nivel de aplicație |
| Jurnale rotite și monitorizate | Detectezi anomaliile devreme |
întărirea nu e un eveniment
Un server securizat astăzi nu rămâne securizat. Apar vulnerabilități noi, adaugi servicii, cineva deschide un port „temporar" și uită. Programează o revizuire trimestrială: verifici ce ascultă pe ce port (ss -tulpn), ce utilizatori există, ce reguli de firewall sunt active, dacă actualizările chiar se aplică și dacă backup-urile chiar rulează.
backup: regula 3-2-1 aplicată pe server
- Ce salvezi: baza de date, fișierele încărcate, configurările din /etc, fișierele de mediu, sarcinile cron.
- Automatizat, prin cron, cu ieșirea trimisă în jurnale și alertă la eșec.
- Criptat dacă pleacă în afara infrastructurii tale.
- Cel puțin o copie în altă locație — un backup pe același server nu te ajută când serverul dispare.
- Snapshot-uri ale întregii mașini, în plus, nu în locul backup-urilor de date.
recuperare după dezastru
Un plan de recuperare răspunde la două întrebări măsurabile:
| Întrebare | Ce stabilește |
| Cât timp pot sta jos? | Cât de repede trebuie să pot reconstrui (ore? minute?) |
| Câte date îmi permit să pierd? | Frecvența backup-urilor |
Răspunsurile decid infrastructura. Dacă îți permiți patru ore de indisponibilitate și o zi de date pierdute, un backup zilnic și o procedură scrisă sunt suficiente. Dacă nu, ai nevoie de replicare și redundanță — cu costul aferent.
exercițiul de restaurare
Cel puțin o dată la câteva luni: reconstruiește serverul de la zero pe o mașină nouă, folosind doar backup-urile și documentația ta. Vei descoperi invariabil lucruri lipsă — o configurare făcută manual acum un an, o cheie care nu e nicăieri, un pas nedocumentat.
Mai bine le descoperi într-un exercițiu planificat decât la 3 dimineața, sub presiune, cu clienții care sună.
răspunsul la un incident de securitate
- Izolează — scoate serverul din trafic; nu-l șterge, ai nevoie de dovezi.
- Evaluează — ce a fost accesat? Ce date sunt afectate?
- Reconstruiește curat. Un server compromis nu se curăță — nu poți fi sigur ce a rămas ascuns. Reinstalezi, apoi restaurezi datele (nu binarele) dintr-un backup anterior compromiterii.
- Rotește toate secretele — parole, chei SSH, token-uri de API, credențiale de bază de date.
- Închide vectorul — află cum s-a intrat, altfel se repetă.
- Notifică dacă au fost afectate date personale — e adesea o obligație legală, cu termene stricte.
de reținut
Întărirea e continuă, nu un eveniment — programează revizuiri trimestriale. Backup-ul urmează 3-2-1, cu o copie obligatoriu în altă locație, iar planul de recuperare se stabilește răspunzând la „cât timp pot sta jos?" și „câte date îmi permit să pierd?". Cel mai valoros exercițiu: reconstruiește serverul de la zero periodic. Iar la un incident: un server compromis se reconstruiește, nu se curăță, și toate secretele se rotesc.
modul 10 / securitate, recuperare · proiect & test final
proiect ghidat: infrastructură completă
Pui tot cursul la lucru construind, de la zero, o infrastructură de producție pentru o aplicație reală — proiectul care demonstrează că poți administra un server cu încredere.
ce construiești
Un VPS care găzduiește o aplicație containerizată cu bază de date, servită prin Nginx cu TLS, publicată automat din Git, monitorizată și cu backup verificat.
planul, pe modulele cursului
- Server (mod. 0): VPS pe distribuție LTS, aproape de utilizatori. Snapshot imediat după creare.
- Securizare inițială (mod. 1–2): utilizator cu sudo, chei SSH verificate în a doua sesiune, parole și root dezactivate, firewall cu SSH permis înainte de activare, fail2ban, actualizări automate. Parcurgi lista de verificare completă.
- Nginx (mod. 3): bloc server, reverse proxy cu toate antetele, TLS prin certbot, compresie, cache pentru statice, antete de securitate, client_max_body_size potrivit.
- Aplicația (mod. 4, 6–7): containerizată, cu Dockerfile optimizat (dependențe înainte de cod, utilizator ne-root, .dockerignore), stivă în Compose cu healthcheck, porturi publicate doar pe 127.0.0.1, limite de resurse.
- Baza de date (mod. 5): în volum persistent, neexpusă, utilizator dedicat cu privilegii minime, utf8mb4, backup automat cu --single-transaction.
- Publicare (mod. 8): pipeline care rulează testele, apoi publică prin SSH; script cu set -euo pipefail, versiuni + legătură simbolică, procedură de revenire testată.
- Monitorizare (mod. 9): alerte pentru disponibilitate, disc >80%, expirare certificat, eșec backup. Jurnale rotite.
- Recuperare (mod. 10): backup în afara serverului, criptat, cu restaurare testată pe o mașină curată.
definiția lui „gata"
- Nu te poți conecta cu parolă și nici ca root; firewall-ul lasă deschise doar 22, 80, 443.
- ss -tulpn nu arată niciun serviciu expus public pe care nu l-ai pus tu acolo intenționat.
- Site-ul răspunde doar pe HTTPS, cu redirecționare 301 și reînnoire automată verificată.
- Un push în ramura principală ajunge în producție fără intervenție manuală, doar dacă testele trec.
- Poți reveni la versiunea anterioară în sub un minut — și ai demonstrat-o.
- Ai restaurat backup-ul pe o mașină nouă și site-ul a funcționat.
- Primești alertă când discul trece de 80% — ai testat că alerta chiar sosește.
- Ai o pagină de documentație care descrie cum se reconstruiește totul de la zero.
ultimul punct e cel mai important
Documentația de reconstrucție e ce transformă o configurare norocoasă într-o infrastructură reală. Dacă totul există doar în capul tău și în comenzi rulate acum șase luni, nu ai un sistem — ai o construcție fragilă care depinde de memoria ta.
Scrie: ce rulează, unde, cu ce configurări, cum se restaurează, unde sunt secretele (nu valorile, ci unde se găsesc) și în ce ordine se pornesc lucrurile. Îți va fi utilă ție peste un an, chiar dacă nimeni altcineva n-o citește vreodată.
felicitări
Ai parcurs administrarea de server cap-coadă: alegerea și securizarea unui VPS, Linux esențial, SSH și firewall, Nginx ca server web și reverse proxy, aplicații cu systemd, baze de date, Docker și Compose, CI/CD, monitorizare, scalare, întărire și recuperare — combinate într-o infrastructură de producție completă. Ai acum ceva rar: înțelegi fiecare strat, de la cererea HTTP până la containerul care o servește. Urmează testul final: 20 de întrebări din tot cursul. Prag: 70%. Succes!