{"id":130,"date":"2025-10-02T00:00:00","date_gmt":"2025-10-01T22:00:00","guid":{"rendered":"https:\/\/helloblog.io\/it\/deploy-wordpress-downtime-zero-con-trellis\/"},"modified":"2026-01-20T06:32:45","modified_gmt":"2026-01-20T05:32:45","slug":"deploy-wordpress-downtime-zero-con-trellis","status":"publish","type":"post","link":"https:\/\/helloblog.io\/it\/deploy-wordpress-downtime-zero-con-trellis\/","title":{"rendered":"Deploy WordPress a downtime zero con Trellis: atomic deploy e rollback immediato"},"content":{"rendered":"\n<p>Nel mondo WordPress il deploy \u00e8 spesso l\u2019anello debole: carichi i file \u201csopra\u201d quelli esistenti e, per qualche minuto, il server serve un mix di versioni vecchie e nuove. Risultato tipico: pagine che vanno in errore, classi PHP mancanti, asset non trovati, cache incoerenti. In ambienti moderni (app web, SaaS, backend) questo problema \u00e8 stato risolto da anni con i cosiddetti <strong>atomic deployments<\/strong> (deploy atomici).<\/p>\n\n\n\n<p>Con Trellis (il provisioning\/deploy tool della famiglia Roots) questo modello \u00e8 disponibile <em>out of the box<\/em> anche per WordPress: prepari una release completa in isolamento e poi \u201cswitchi\u201d in un istante. Il sito resta raggiungibile durante tutto il processo e, se qualcosa va storto, il rollback diventa un\u2019operazione banale.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Cosa significa davvero \u201czero downtime\u201d in fase di deploy<\/h2>\n\n\n\n<p>\u201cZero downtime\u201d non vuol dire che il deploy \u00e8 pi\u00f9 veloce: vuol dire che <strong>gli utenti non incontrano mai uno stato intermedio<\/strong>. Il codice nuovo viene assemblato in una directory separata (dipendenze incluse) e solo quando \u00e8 pronto si effettua il passaggio alla nuova versione con uno switch immediato.<\/p>\n\n\n\n<p>Il punto chiave \u00e8 eliminare l\u2019idea di \u201csovrascrivere file mentre il sito \u00e8 online\u201d. Sovrascrivere in-place \u00e8 comodo, ma \u00e8 anche la causa principale di comportamenti strani durante gli aggiornamenti: met\u00e0 tema nuovo, met\u00e0 tema vecchio; plugin aggiornato ma autoloader non coerente; asset compilati che non combaciano con l\u2019HTML gi\u00e0 in cache.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Perch\u00e9 il deploy \u201cclassico\u201d di WordPress crea problemi<\/h2>\n\n\n\n<p>Nella pratica, tanti siti WordPress finiscono in produzione con uno di questi flussi:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li><strong>FTP\/SFTP<\/strong>: upload manuale dei file modificati, sovrascrivendo quelli esistenti. \u00c8 facile servire un mix di file vecchi e nuovi per diversi minuti.<\/li>\n\n\n<li><strong>Sincronizzazione file<\/strong> (es. <code>rsync<\/code>): pi\u00f9 veloce dell\u2019FTP, ma concettualmente identico: stai comunque sovrascrivendo mentre il sito risponde.<\/li>\n\n\n<li><strong>Deploy via plugin<\/strong> su host managed: spesso \u00e8 comodo, ma di frequente aggiorna i file \u201cin place\u201d e non offre un rollback vero e immediato.<\/li>\n\n<\/ul>\n\n\n\n<p>Questi approcci hanno tre difetti ricorrenti: (1) rischio reale di downtime o errori transienti, (2) rollback macchinoso quando qualcosa non va, (3) nessuna separazione netta tra fase di build\/preparazione e fase di switch.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Come Trellis implementa gli atomic deploy (immutabili) con una struttura a release<\/h2>\n\n\n\n<p>Trellis adotta un modello mutuato dai deploy di applicazioni moderne: ogni deploy genera una <strong>release immutabile<\/strong> (i file di quella release, una volta pubblicata, non vengono pi\u00f9 modificati). Il traffico punta sempre a un\u2019unica \u201centry\u201d stabile: un symlink chiamato <code>current<\/code>.<\/p>\n\n\n\n<p>A livello di filesystem, su server trovi una struttura simile a questa:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#e1e4e8;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>\/srv\/www\/example.com\/\n\u251c\u2500\u2500 current\/             # Symlink alla release attiva\n\u251c\u2500\u2500 releases\/            # Tutte le release deployate\n\u2502   \u251c\u2500\u2500 20250930124530\/\n\u2502   \u251c\u2500\u2500 20250930083045\/\n\u2502   \u2514\u2500\u2500 20250930141622\/  # La pi\u00f9 recente\n\u251c\u2500\u2500 shared\/              # File condivisi tra release\n\u2502   \u2514\u2500\u2500 uploads\/\n\u2514\u2500\u2500 logs\/\n<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#B392F0\">\/srv\/www\/example.com\/<\/span><\/span>\n<span class=\"line\"><span style=\"color:#B392F0\">\u251c\u2500\u2500<\/span><span style=\"color:#9ECBFF\"> current\/<\/span><span style=\"color:#6A737D\">             # Symlink alla release attiva<\/span><\/span>\n<span class=\"line\"><span style=\"color:#B392F0\">\u251c\u2500\u2500<\/span><span style=\"color:#9ECBFF\"> releases\/<\/span><span style=\"color:#6A737D\">            # Tutte le release deployate<\/span><\/span>\n<span class=\"line\"><span style=\"color:#B392F0\">\u2502<\/span><span style=\"color:#9ECBFF\">   \u251c\u2500\u2500<\/span><span style=\"color:#9ECBFF\"> 20250930124530\/<\/span><\/span>\n<span class=\"line\"><span style=\"color:#B392F0\">\u2502<\/span><span style=\"color:#9ECBFF\">   \u251c\u2500\u2500<\/span><span style=\"color:#9ECBFF\"> 20250930083045\/<\/span><\/span>\n<span class=\"line\"><span style=\"color:#B392F0\">\u2502<\/span><span style=\"color:#9ECBFF\">   \u2514\u2500\u2500<\/span><span style=\"color:#9ECBFF\"> 20250930141622\/<\/span><span style=\"color:#6A737D\">  # La pi\u00f9 recente<\/span><\/span>\n<span class=\"line\"><span style=\"color:#B392F0\">\u251c\u2500\u2500<\/span><span style=\"color:#9ECBFF\"> shared\/<\/span><span style=\"color:#6A737D\">              # File condivisi tra release<\/span><\/span>\n<span class=\"line\"><span style=\"color:#B392F0\">\u2502<\/span><span style=\"color:#9ECBFF\">   \u2514\u2500\u2500<\/span><span style=\"color:#9ECBFF\"> uploads\/<\/span><\/span>\n<span class=\"line\"><span style=\"color:#B392F0\">\u2514\u2500\u2500<\/span><span style=\"color:#9ECBFF\"> logs\/<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Il web server serve sempre e solo quello che c\u2019\u00e8 dietro <code>current<\/code>. Quando arriva una nuova release, Trellis la prepara in <code>releases\/<timestamp><\/code> e poi aggiorna il symlink <code>current<\/code> con un\u2019operazione praticamente istantanea.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Cosa succede quando lanci <code>trellis deploy production<\/code><\/h3>\n\n\n\n<p>Durante un deploy, Trellis esegue una pipeline ben definita (e personalizzabile):<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li><strong>Initialize<\/strong>: verifica\/crea la struttura e prepara una nuova directory release con timestamp.<\/li>\n\n\n<li><strong>Update<\/strong>: clona il codice pi\u00f9 recente dal repository Git in un\u2019area separata dal sito live.<\/li>\n\n\n<li><strong>Prepare<\/strong>: copia i file nella nuova directory di release.<\/li>\n\n\n<li><strong>Build<\/strong>: esegue <code>composer install<\/code> per installare le dipendenze PHP.<\/li>\n\n\n<li><strong>Share<\/strong>: collega (via symlink) i contenuti condivisi, ad esempio <code>uploads<\/code>, dalla directory <code>shared<\/code> alla nuova release.<\/li>\n\n\n<li><strong>Finalize<\/strong>: aggiorna <code>current<\/code> per puntare alla nuova release.<\/li>\n\n<\/ul>\n\n\n\n<p>Il salto tra la release precedente e quella nuova avviene senza \u201cmescolare\u201d file: un attimo prima stai servendo <code>releases\/20250930124530<\/code>, un attimo dopo <code>releases\/20250930141622<\/code>. \u00c8 questo il cuore del deploy atomico.<\/p>\n\n\n\n<div class=\"wp-block-group callout callout-info is-style-info is-layout-flow wp-block-group-is-layout-flow\" style=\"border-width:1px;border-radius:8px;padding-top:1rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem\">\n\n<h4 class=\"wp-block-heading callout-title\">Trellis solo per il deploy<\/h4>\n\n\n<p>Non sei obbligato a usare Trellis per tutto il workflow di sviluppo. Molti lo usano solo come strato di deploy per progetti WordPress basati su Bedrock, mantenendo in locale l\u2019ambiente preferito (Valet, Lando, DDEV, ecc.) e distribuendo su provider diversi (anche managed).<\/p>\n\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Database: lo \u201czero downtime\u201d del codice non risolve automaticamente le migrazioni<\/h2>\n\n\n\n<p>Un punto da tenere a mente: Trellis gestisce lo <strong>zero downtime sul filesystem<\/strong> (codice, dipendenze, struttura di release). Le modifiche di schema o trasformazioni dati nel database sono un tema separato. Nella documentazione Trellis viene chiarito che le migrazioni di database verso un nuovo schema non sono incluse automaticamente nel deploy.<\/p>\n\n\n\n<p>Se nel tuo progetto usi Acorn (il layer application di Roots che porta pattern in stile Laravel), puoi gestire <strong>Laravel migrations<\/strong> anche in contesto WordPress e integrarle nel processo di rilascio. In ogni caso, il principio operativo resta: progettare migrazioni compatibili (forward\/backward) quando vuoi mantenere l\u2019assenza di interruzioni percepibili.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Rollback immediato: quando ogni release \u00e8 completa e immutabile<\/h2>\n\n\n\n<p>Il vantaggio pratico pi\u00f9 sottovalutato degli atomic deploy \u00e8 il rollback. Se ogni release \u00e8 una directory completa e non viene mai modificata dopo il deploy, tornare indietro significa semplicemente ripuntare <code>current<\/code> alla release precedente.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#e1e4e8;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>trellis rollback production\n<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#B392F0\">trellis<\/span><span style=\"color:#9ECBFF\"> rollback<\/span><span style=\"color:#9ECBFF\"> production<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>In base alla configurazione di default, Trellis mantiene sul server le cinque release pi\u00f9 recenti. Questo ti d\u00e0 un paracadute operativo molto concreto: non devi \u201crimettere a posto\u201d file a mano n\u00e9 sperare che la cache si riallinei da sola.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Hook di deploy: personalizzare build, controlli e operazioni post-rilascio<\/h2>\n\n\n\n<p>Un altro aspetto utile \u00e8 la presenza di hook (punti di estensione) per inserire task specifici in varie fasi. Tra quelli pi\u00f9 usati:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li><code>deploy_build_before<\/code> e <code>deploy_build_after<\/code> per aggiungere step di build personalizzati.<\/li>\n\n\n<li><code>deploy_finalize_before<\/code> e <code>deploy_finalize_after<\/code> per task subito prima\/dopo lo switch della release.<\/li>\n\n\n<li>Hook per ogni fase principale: initialize, update, prepare, build, share, finalize.<\/li>\n\n<\/ul>\n\n\n\n<p>Questi hook abilitano pattern molto pratici in progetti reali: backup del database prima del deploy, purge di cache applicative o CDN dopo il rilascio, notifiche al team, smoke test automatici contro la nuova release prima di considerarla \u201cbuona\u201d.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Setup minimo: Bedrock + Trellis e primo deploy<\/h2>\n\n\n\n<p>Per partire con questo approccio, la combinazione tipica nell\u2019ecosistema Roots \u00e8:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n\n<li>Impostare il progetto con <strong>Bedrock<\/strong> per una struttura WordPress pi\u00f9 pulita e orientata a Composer.<\/li>\n\n\n<li>Installare <strong>Trellis<\/strong> e configurare i parametri di deploy.<\/li>\n\n\n<li>Configurare <code>wordpress_sites.yml<\/code> con le info del repository Git.<\/li>\n\n\n<li>Eseguire il deploy con <code>trellis deploy production<\/code>.<\/li>\n\n<\/ol>\n\n\n\n<p>Il primo deploy tende a durare di pi\u00f9 perch\u00e9 inizializza la struttura e scarica tutte le dipendenze. I deploy successivi diventano pi\u00f9 rapidi e, soprattutto, evitano quella finestra in cui il sito rischia di servire file incoerenti.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">In sintesi: meno rischio operativo, pi\u00f9 controllo<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li>Gli atomic deploy eliminano lo stato intermedio: niente mix di file vecchi e nuovi in produzione.<\/li>\n\n\n<li>La struttura a release con symlink <code>current<\/code> rende lo switch istantaneo e prevedibile.<\/li>\n\n\n<li>Rollback davvero immediato: basta ripuntare alla release precedente.<\/li>\n\n\n<li>Hook di deploy per inserire controlli, backup, cache purge e automazioni mirate.<\/li>\n\n\n<li>Il tema \u201cdatabase\u201d resta separato: gestiscilo con attenzione (e con Acorn\/migrations se rientra nel tuo stack).<\/li>\n\n<\/ul>\n\n\n<div class=\"references-section\">\n                <h2>Riferimenti \/ Fonti<\/h2>\n                <ul class=\"references-list\"><li><a href=\"https:\/\/roots.io\/zero-downtime-wordpress-deployments-with-trellis\/\" target=\"_blank\" rel=\"noopener noreferrer\">Zero Downtime WordPress Deployments with Trellis<\/a><\/li><li><a href=\"https:\/\/roots.io\/trellis\/docs\/deployments\/\" target=\"_blank\" rel=\"noopener noreferrer\">Trellis docs \u2014 Deployments<\/a><\/li><li><a href=\"https:\/\/roots.io\/acorn\/docs\/creating-and-running-laravel-migrations\/\" target=\"_blank\" rel=\"noopener noreferrer\">Acorn docs \u2014 Creating and running Laravel migrations<\/a><\/li><\/ul>\n            <\/div>","protected":false},"excerpt":{"rendered":"<p>Se ti \u00e8 mai capitato di vedere un sito WordPress \u201ca pezzi\u201d durante un aggiornamento via FTP o rsync, non \u00e8 sfortuna: \u00e8 proprio il modello di deploy. Con Trellis puoi passare a un approccio atomic che elimina lo stato intermedio e ti d\u00e0 anche un rollback istantaneo.<\/p>\n","protected":false},"author":18,"featured_media":129,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[68],"tags":[70,71,3,69,10],"class_list":["post-130","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-infrastruttura","tag-deploy","tag-devops","tag-roots","tag-trellis","tag-wordpress"],"_links":{"self":[{"href":"https:\/\/helloblog.io\/it\/wp-json\/wp\/v2\/posts\/130","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/helloblog.io\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/helloblog.io\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/helloblog.io\/it\/wp-json\/wp\/v2\/users\/18"}],"replies":[{"embeddable":true,"href":"https:\/\/helloblog.io\/it\/wp-json\/wp\/v2\/comments?post=130"}],"version-history":[{"count":1,"href":"https:\/\/helloblog.io\/it\/wp-json\/wp\/v2\/posts\/130\/revisions"}],"predecessor-version":[{"id":146,"href":"https:\/\/helloblog.io\/it\/wp-json\/wp\/v2\/posts\/130\/revisions\/146"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/helloblog.io\/it\/wp-json\/wp\/v2\/media\/129"}],"wp:attachment":[{"href":"https:\/\/helloblog.io\/it\/wp-json\/wp\/v2\/media?parent=130"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/helloblog.io\/it\/wp-json\/wp\/v2\/categories?post=130"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/helloblog.io\/it\/wp-json\/wp\/v2\/tags?post=130"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}