Cloud

GitHub Actions Deploy Server Linux: Guida Automatica in 20 Minuti (2026)

GitHub Actions Deploy Server Linux: Guida Automatica in 20 Minuti (2026)

GitHub Actions automatizza il deploy su server Linux via SSH usando secrets per le credenziali. Configuri un workflow YAML che si attiva al push sul branch main, esegue comandi remoti e gestisce il rollback se l’operazione fallisce, eliminando gli errori manuali.

| Componente YAML | Scopo | Esempio |

|—|—|—|

| on.push.branches | Trigger del workflow | [main] |

| runs-on | OS del runner temporaneo | ubuntu-latest |

| appleboy/ssh-action | Connessione SSH remota | master |

| secrets.SSH_KEY | Chiave privata criptata | ${{ secrets.* }} |

| environment | Approval gate richiesto | production |

Deploy manuale su 12 server ogni settimana. Copia, incolla, spera che tutto funzioni. Un venerdì pomeriggio ho caricato un file di configurazione sbagliato su 4 server di produzione prima di accorgersi dell’errore. Il downtime è durato 40 minuti. GitHub Actions con approval gate avrebbe fermato il deploy prima che il file raggiungesse i server. Il 45% degli incidenti in produzione deriva da errori di configurazione manuali, secondo il Puppet State of DevOps Report 2024. Ho configurato il primo workflow automatizzato in 20 minuti e non ho più fatto deploy manuali su quei server.

Prerequisiti / Ambiente di test

Prima di iniziare, ti serve un server Linux accessibile via SSH e un repository GitHub con il codice da deployare. Sul server, installa git e configura l’applicazione (Node.js per questa guida). Sul tuo computer locale, installa git e genera una chiave SSH dedicata al deploy.

Struttura di un workflow GitHub Actions

Un workflow è un file YAML dentro la cartella .github/workflows/ del tuo repository. GitHub lo esegue automaticamente quando si verifica l’evento specificato nel blocco on. Il blocco jobs contiene i passaggi eseguiti su un runner temporaneo, un container Ubuntu che GitHub crea, usa e distrugge in pochi minuti. Ogni step dentro il job esegue un’azione o un comando shell.

name: Deploy Production
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Deploy via SSH
        uses: appleboy/ssh-action@master

Il runner scarica il codice con actions/checkout, poi usa appleboy/ssh-action per connettersi al tuo server Linux ed eseguire i comandi di deploy.

Configurare i secrets (SSH key, server IP, utente)

Mai scrivere password o chiavi private nel codice. Il 73% dei breach parte da credenziali rubate, spesso hardcoded nei file di configurazione, come riporta il Verizon DBIR 2025. GitHub risolve questo problema con i secrets. Vai in Settings > Secrets and variables > Actions nel tuo repository e crea tre secrets:

  1. SERVER_IP: l’indirizzo IP pubblico del server Linux.
  2. SERVER_USER: l’utente SSH per il deploy, esempio deployuser.
  3. SSH_KEY: la chiave privata SSH dedicata.

Genera la chiave sul tuo computer locale:

ssh-keygen -t ed25519 -C "github-deploy" -f ~/.ssh/github_deploy_key

Copia il contenuto del file github_deploy_key.pub nel file ~/.ssh/authorized_keys del server Linux. Copia il contenuto del file github_deploy_key (la chiave privata) nel secret SSH_KEY su GitHub. Ora il runner può autenticarsi senza password.

Workflow base: connessione SSH ed esecuzione comandi

Il workflow base connette il runner al server ed esegue comandi remoti. Usa l’azione appleboy/ssh-action, che gestisce l’autenticazione e l’esecuzione in modo sicuro.

# .github/workflows/deploy.yml
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Deploy via SSH
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SERVER_IP }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            cd /var/www/myapp
            git pull origin main
            echo "Deploy completato"

Quando pushi sul branch main, GitHub crea il runner, legge i secrets, si connette al server e esegue i comandi definiti in script. Se un comando fallisce, il workflow si interrompe e GitHub marca il job come errore.

Deploy di un’app Node.js

Per un’app Node.js, aggiungi i comandi di installazione e riavvio dopo il git pull. Usa pm2 per gestire il processo in background.

      - name: Deploy Node.js via SSH
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SERVER_IP }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            cd /var/www/nodeapp
            git pull origin main
            npm install --production
            npm run build
            pm2 restart nodeapp --update-env
            pm2 save

Il runner esegue il pull, installa le dipendenze senza i pacchetti di development, compila il codice e riavvia il processo con pm2. Il flag --update-env refreshing le variabili d’ambiente. Le organizzazioni che adottano CI/CD riducono i tempi di deploy del 60% e i fallimenti in produzione del 30%, secondo il DORA State of DevOps 2025.

Deploy di un sito statico

Per i siti statici, non serve git pull sul server. Compila localmente nel runner e trasferisci i file con scp o rsync.

jobs:
  deploy-static:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build static site
        run: npm install && npm run build
      - name: Transfer files via SSH
        uses: appleboy/scp-action@master
        with:
          host: ${{ secrets.SERVER_IP }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_KEY }}
          source: "dist/*"
          target: "/var/www/html"

L’azione appleboy/scp-action copia i file compilati nella cartella dist direttamente nella webroot del server, sostituendo i file vecchi.

Rollback automatico se il deploy fallisce

Se il deploy fallisce, devi tornare alla versione precedente in pochi secondi. Usa una strategia basata su symlink. Mantieni due cartelle: current (symlink alla versione attiva) e releases (cartelle numerate per versione).

      - name: Rollback on failure
        if: failure()
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SERVER_IP }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            ln -sfn /var/www/releases/previous /var/www/current
            pm2 restart nodeapp
            echo "Rollback eseguito"

Il blocco if: failure() attiva questo step solo se uno dei passaggi precedenti fallisce. Il symlink punta all’ultima versione stabile. Il servizio riparte con il codice funzionante.

Notifiche Telegram/email al completamento

Ricevere un alert quando il deploy finisce ti permette di intervenire subito se qualcosa va storto. Configura un bot Telegram e usa curl nel workflow.

      - name: Notify Telegram on success
        if: success()
        run: |
          curl -s -X POST https://api.telegram.org/bot${{ secrets.TELEGRAM_TOKEN }}/sendMessage \n          -d chat_id=${{ secrets.TELEGRAM_CHAT_ID }} \n          -d text="Deploy completato su ${{ secrets.SERVER_IP }}"

Aggiungi TELEGRAM_TOKEN e TELEGRAM_CHAT_ID ai secrets del repository. Per il fallimento, duplica lo step con if: failure() e cambia il testo dell’alert.

Limitare il deploy al branch main

Il blocco on: push: branches: [main] limita il trigger al branch principale. Se vuoi un controllo extra, usa gli environment protection rules. Crea un environment chiamato production in Settings > Environments. Aggiungi un required reviewer, un collega che deve approvare il deploy prima che il runner parta.

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4

Quando pushi su main, GitHub ferma il workflow e invia una notifica al reviewer. Il reviewer clicca Approve. Solo allora il runner si connette al server. Questo approval gate avrebbe fermato il venerdì pomeriggio con il file di configurazione sbagliato, impedendo al codice difettoso di raggiungere i 4 server.

Errori comuni e troubleshooting

Permission denied (publickey): il runner non accede al server. Controlla che la chiave pubblica sia nel file authorized_keys dell’utente remoto e che la chiave privata nel secret SSH_KEY includa l’intero contenuto del file, compresa la riga finale.

Host key verification failed: il runner non conosce l’ fingerprint del server. Aggiungi script_stop: true e configura debug: true in appleboy/ssh-action per vedere l’output completo della connessione SSH.

Secrets non trovati: il workflow restituisce errori sulle variabili. Verifica che i nomi dei secrets in Settings corrispondano esattamente a quelli nel YAML. Un carattere diverso rende il secret invisibile al runner.

Comandi lunghi che si bloccano: SSH chiude la connessione se un comando non produce output per molto tempo. Aggiungi opzioni di timeout o divide i comandi in step separati.

FAQ: Domande Frequenti

Come generare la chiave SSH per GitHub Actions senza password?

Esegui ssh-keygen -t ed25519 -C "github-deploy" e premi Invio quando chiede la password. Una chiave senza passphrase permette al runner di autenticarsi in automatico. Limita questa chiave solo al deploy su un utente con permessi ridotti.

Posso deployare su più server contemporaneamente?

Sì. Usa la matrix strategy di GitHub Actions per definire una lista di server. Il runner crea job paralleli, uno per ogni IP, e esegue il deploy su tutti i server nello stesso momento.

GitHub Actions è gratuito per progetti privati?

GitHub offre 2.000 minuti gratuiti al mese per account free e 3.000 per account Pro. Un workflow di deploy via SSH dura circa 2 minuti. Puoi eseguire circa 1.000 deploy al mese senza costi aggiuntivi.

Come funziona l’approval gate in GitHub Actions?

Crea un environment in Settings > Environments, aggiungi required reviewers. Quando il workflow raggiunge lo step con quell’environment, GitHub ferma l’esecuzione e invia una notifica. Il reviewer approva dalla interfaccia web o dall’app mobile.

Conclusioni con takeaway operativi

  1. Sostituisci il deploy manuale con GitHub Actions. Il workflow YAML elimina la copia incolla e riduce gli errori umani del 45%.
  2. Usa i secrets per le credenziali SSH. Niente chiavi private nel repository, niente password in chiaro nei log.
  3. Aggiungi rollback automatico e approval gate. Se il deploy fallisce, torni alla versione stabile. Se il codice è sbagliato, un collega lo ferma prima che arrivi in produzione.

Leggi anche: Docker Tutorial Sysadmin: Primo Container in Produzione (2026)

Leggi anche: Configurare un VPS Linux da Zero: Checklist 2026

Leggi anche: WireGuard VPN Linux: Configurare in 15 Minuti (2026)

Aggiornato: Maggio 2026

Condividi questo articolo:

Scritto da

Rosario Giordano

Rosario Giordano è System Administrator e consulente IT specializzato in cybersecurity e cloud, con oltre 20 anni di esperienza nella gestione di infrastrutture Linux enterprise. Le sue aree di competenza includono hardening di SSH, piattaforme Kubernetes, database PostgreSQL, ambient i virtualizzati VMware e Proxmox, nonché la conformità ai framework di sicurezza NIS2 e ISO 27001.