Migrare da hosting condiviso a VPS per Laravel: perché, quando e come farlo senza perdere dati

Migrare da hosting condiviso a VPS per Laravel: perché, quando e come farlo senza perdere dati

Nella mia esperienza di consulente su infrastrutture Laravel per PMI italiane, la migrazione da hosting condiviso a VPS è uno dei momenti più delicati e sottovalutati del ciclo di vita di un'applicazione. Lo dico perché ho gestito personalmente questa transizione in contesti molto diversi - dall'e-commerce B2B con 400 SKU e ordini giornalieri, al gestionale interno di un'azienda manifatturiera con 80 utenti concorrenti, fino alla piattaforma SaaS di una startup che aveva superato i limiti del suo piano hosting senza rendersene conto per mesi.

Il pattern è quasi sempre lo stesso: l'applicazione nasce su un hosting condiviso perché costa poco e "per ora basta", cresce nel tempo assorbendo complessità (queue, scheduled task, cache Redis, storage file), e a un certo punto inizia a manifestare sintomi - rallentamenti intermittenti, timeout sulle API, job che si accavallano, sessioni che scadono senza motivo. Il team di sviluppo (o più spesso l'unico sviluppatore freelance) attribuisce il problema al codice, ottimizza query, aggiunge indici, riduce il payload delle risposte. Ma il codice non è il collo di bottiglia: lo è l'infrastruttura condivisa che non può garantire risorse dedicate.

Perché un hosting condiviso è inadeguato per Laravel in produzione?

La risposta breve è: isolamento insufficiente e controllo assente. La risposta lunga richiede di capire cosa succede realmente sotto la superficie di un hosting condiviso quando ci metti sopra un'applicazione Laravel non banale.

Su un hosting condiviso, la tua applicazione PHP gira sullo stesso server di decine o centinaia di altri siti. Le risorse (CPU, RAM, I/O disco) sono ripartite con meccanismi di throttling che il provider non ti documenta. PHP viene eseguito in una configurazione standardizzata che non puoi modificare: la versione è quella che il provider decide di supportare, le estensioni disponibili sono quelle che servono alla maggioranza dei clienti, i limiti di memory_limit, max_execution_time e upload_max_filesize sono impostati per non far crollare il server condiviso - non per far funzionare la tua applicazione.

La documentazione ufficiale PHP sulla sicurezza è esplicita su questo punto: in un ambiente condiviso, open_basedir non è una soluzione di sicurezza completa. Le librerie di terze parti possono essere indotte ad accedere a file al di fuori del perimetro previsto, e l'isolamento tra tenant è strutturalmente debole. Per applicazioni critiche, PHP stesso raccomanda di eseguire web server separati, ciascuno con il proprio user ID, idealmente in filesystem chroot-ati.

Nel contesto di un'applicazione Laravel che gestisce dati aziendali - fatture, anagrafiche clienti, credenziali di accesso, documenti - questa raccomandazione non è opzionale. È un requisito minimo.

Se stai valutando la migrazione della tua infrastruttura Laravel e vuoi un parere tecnico indipendente, nel mio profilo professionale trovi l'esperienza concreta su stack Laravel in produzione e gestione VPS.

I segnali concreti che indicano la necessità di migrare

Nel mio lavoro, ho imparato a riconoscere i segnali che precedono il punto di rottura. Non sono sempre evidenti, perché il degrado è graduale:

Timeout intermittenti sulle API. Laravel risponde in 200ms la mattina e in 3 secondi nel pomeriggio. Il motivo è che un altro sito sullo stesso server sta eseguendo un cron job pesante, e il tuo php-fpm viene strozzato dal kernel. Non c'è nulla che tu possa fare lato codice.

Queue worker che si bloccano o ripartono. Se usi php artisan queue:work su un hosting condiviso (ammesso che il provider lo permetta), il processo viene killato periodicamente dal sistema di resource enforcement. I job restano in stato reserved e nessuno li processa. Su un VPS, il queue worker gira sotto supervisor con restart automatico e log dedicati.

Impossibilità di aggiornare PHP. La tua applicazione Laravel 10+ richiede PHP 8.1 come minimo. Il provider condiviso offre PHP 8.0 perché "è la versione stabile". Non puoi installare estensioni (imagick, redis, swoole), non puoi modificare php.ini, non puoi compilare moduli custom. Sei bloccato.

Backup non verificabili. Il provider dice che fa backup giornalieri. Tu non li hai mai testati. Non sai dove sono, non sai come ripristinarli, non sai in quanto tempo. Quando serve davvero - e nella mia esperienza serve sempre nel momento peggiore - scopri che il backup è vecchio di 72 ore e non include il database.

Quando osservo due o più di questi segnali in un audit iniziale, la raccomandazione è sempre la stessa: migrare su un VPS dove hai pieno controllo. Il costo mensile è spesso comparabile (un VPS entry-level su Hetzner parte da meno di 5 euro al mese per 2 vCPU e 4 GB di RAM), ma la differenza in termini di affidabilità, sicurezza e autonomia operativa è enorme.

Se gestisci un'infrastruttura cloud per applicazioni business-critical, Hetzner rappresenta un'opzione concreta per rapporto qualità/prezzo, conformità GDPR (data center in Germania e Finlandia) e affidabilità comprovata. Digital Ocean, Contabo, OVH e Aruba sono alternative valide con posizionamenti diversi. Hetzner offre un credito di 20 euro per nuovi account, sufficiente per testare un VPS CX22 per oltre un mese.

La procedura operativa che applico nelle migrazioni reali

Ogni migrazione che gestisco segue una sequenza rigida in cinque fasi. Non è un protocollo teorico - è il risultato di errori che ho commesso e corretto negli anni, cristallizzato in un processo che minimizza il rischio di downtime e data loss.

Fase 1: inventario completo dell'ambiente sorgente

Prima di toccare qualsiasi cosa, mappo lo stato attuale. Questo significa:

php artisan --version
php -v
php -m | sort
composer show --installed | wc -l
mysql -V
df -h
du -sh storage/app/ storage/framework/ storage/logs/
crontab -l

Documento versione PHP esatta, estensioni installate, numero di dipendenze Composer, versione MySQL, spazio disco occupato da storage e log, cron job attivi. Questo inventario mi serve per due cose: configurare il VPS target con lo stesso stack, e identificare in anticipo le incompatibilità (estensioni mancanti, versioni PHP divergenti, storage locale che dev'essere migrato su object storage).

Fase 2: provisioning del VPS con hardening base

Il VPS target viene provvisionato con Debian 12 o Ubuntu 24.04 LTS. La prima cosa che faccio non è installare Laravel - è hardenizzare il server:

apt update && apt upgrade -y

# Utente non-root per il deploy
adduser deploy
usermod -aG sudo deploy

# SSH solo con chiavi, no password, no root login
sed -i 's/#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart ssh

# Firewall: solo SSH, HTTP, HTTPS
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

Solo dopo l'hardening installo lo stack applicativo: Nginx, PHP-FPM (versione corrispondente all'inventario), MySQL o MariaDB, Redis, Composer, Certbot per SSL. La documentazione ufficiale di Laravel sul deployment elenca i requisiti server e le ottimizzazioni obbligatorie per la produzione - la seguo alla lettera.

Fase 3: trasferimento dati con verifica di integrità

Il trasferimento non è un semplice scp. La sequenza è:

# Sul VPS target, come utente deploy
git clone <repository> /var/www/app
cd /var/www/app
composer install --no-dev --optimize-autoloader

# Database: dump strutturato con lock per consistenza
# Sull'hosting sorgente
mysqldump --single-transaction --routines --triggers \
  -u user -p database > dump_$(date +%Y%m%d_%H%M%S).sql

# Sul VPS target
mysql -u root -p -e "CREATE DATABASE app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
mysql -u root -p app < dump.sql

# Verifica: conteggio record sulle tabelle principali
mysql -u root -p app -e "SELECT 'users' AS t, COUNT(*) AS n FROM users
  UNION ALL SELECT 'orders', COUNT(*) FROM orders
  UNION ALL SELECT 'products', COUNT(*) FROM products;"

La verifica del conteggio record è un passaggio che molti saltano, e che mi ha salvato più volte. In un caso specifico, il dump dall'hosting condiviso si era interrotto silenziosamente a metà della tabella order_items - 12.000 righe su 48.000. Senza il conteggio incrociato, il cliente avrebbe scoperto il problema solo quando un ristoratore avrebbe cercato lo storico ordini del 2024.

Fase 4: configurazione applicativa e warm-up cache

cp .env.example .env
# Configurazione manuale di DB_*, REDIS_*, MAIL_*, APP_URL
php artisan key:generate
php artisan migrate --force
php artisan optimize
php artisan storage:link

Il comando php artisan optimize - introdotto come comando unificato nelle versioni recenti di Laravel - compila in cache configurazione, route, view ed eventi in un'unica operazione. In produzione è obbligatorio: riduce i tempi di bootstrap del 40-60% eliminando il parsing dei file a ogni richiesta.

Fase 5: cutover DNS e verifica post-migrazione

Il cutover è il momento in cui il dominio punta al nuovo server. Lo faccio sempre di sera o nel weekend, con un TTL DNS abbassato a 300 secondi almeno 48 ore prima. Dopo il cutover, verifico:

  • Risposta HTTP 200 su tutte le route principali
  • Login funzionante (sessioni, CSRF token)
  • Job in coda che vengono processati (php artisan queue:monitor)
  • Certificato SSL attivo e valido (certbot --nginx -d dominio.it)
  • Backup automatico configurato e primo backup eseguito con successo

Se tutto è green, alzo il TTL DNS a 3600 secondi e disattivo il vecchio hosting. Non lo cancello per almeno 30 giorni - è il mio fallback estremo.

Cosa fare dopo la migrazione: il debito tecnico non finisce qui

La migrazione è solo il primo passo. Un VPS senza manutenzione è più pericoloso di un hosting condiviso, perché ti dà l'illusione del controllo senza l'obbligo di esercitarlo. L'OWASP Top 10 2025 classifica la Security Misconfiguration come il secondo rischio più critico per le applicazioni web, con un tasso di incidenza del 100% nei test effettuati. Su un VPS, la responsabilità dell'hardening è interamente tua.

Dopo ogni migrazione, implemento un piano post-migrazione che include:

Backup automatici verificati. Non backup "che dovrebbero funzionare" - backup che vengono testati con ripristino periodico. Ho documentato le strategie avanzate in un articolo dedicato ai backup VPS su Hetzner, OVH, Contabo, Digital Ocean e Aruba.

Hardening applicativo e infrastrutturale. Firewall, fail2ban, aggiornamenti automatici di sicurezza, header HTTP (CSP, HSTS, X-Frame-Options), rate limiting. La checklist completa per applicazioni Laravel e Symfony è documentata nella guida all'hardening NIS2-ready.

Monitoring proattivo. CPU, RAM, disco, latenza delle risposte HTTP. Prometheus + Grafana su un secondo VPS dedicato, o servizi SaaS come Uptime Robot per il monitoring base. L'obiettivo è sapere che qualcosa si sta rompendo prima che il cliente te lo segnali.

Pipeline CI/CD. Anche minimale - un deploy script con git pull, composer install --no-dev, php artisan optimize, php artisan migrate --force. Automatizzare il deploy elimina la classe di errori "ho fatto un deploy manuale e mi sono dimenticato di eseguire le migration".

Per chi prevede una migrazione tra provider in futuro, ho documentato anche la procedura di migrazione VPS a zero downtime.

Il costo reale della non-migrazione

Le PMI che rimandano la migrazione da hosting condiviso a VPS lo fanno quasi sempre per una ragione: "funziona ancora, perché cambiare?". È una domanda legittima, e la risposta è nei numeri.

Un hosting condiviso costa tipicamente 5-15 euro al mese. Un VPS equivalente su Hetzner costa 4-8 euro al mese. La differenza di costo infrastrutturale è spesso zero o negativa. Il costo reale è nel tempo di migrazione e nella competenza necessaria per farla bene.

Ma il costo della non-migrazione è molto più alto. Nel mio lavoro ho visto aziende perdere ordini per settimane a causa di timeout intermittenti attribuiti a "problemi di rete", quando il problema era un hosting condiviso saturo. Ho visto applicazioni compromesse perché un altro tenant sullo stesso server aveva una vulnerabilità sfruttata, e l'attaccante aveva fatto lateral movement sulle directory condivise. Ho visto backup dichiarati dal provider risultare inutilizzabili al momento del ripristino.

Questi non sono scenari ipotetici. Sono situazioni che ho gestito personalmente, e che si sarebbero evitate con una migrazione pianificata su VPS al momento giusto.

Se stai gestendo un'applicazione Laravel su hosting condiviso e riconosci i segnali che ho descritto - timeout, queue instabili, impossibilità di aggiornare, backup opachi - il momento di migrare è adesso, non dopo il prossimo incidente. Contattami per valutare insieme la situazione e pianificare la transizione con un approccio strutturato che protegga i tuoi dati e la continuità del tuo business.

Ultima modifica: