SSD Nodes Learn Hosting plans →
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-08-27

Self-hosting n8n su VPS: Docker e HTTPS

Configura n8n su VPS con Docker Compose, Postgres e HTTPS dietro reverse proxy. Evita i problemi di WEBHOOK_URL, encryption key e gli errori più comuni.

Cosa stai costruendo

n8n è uno strumento per l'automazione dei workflow: un editor visuale in cui un trigger, un webhook, una pianificazione o l'invio di un modulo avvia una catena di nodi che chiamano API, trasformano i dati e li scrivono in altri sistemi. È diventato lo strumento di integrazione predefinito per i workflow con agenti AI, perché comunica con ogni provider di modelli e database senza richiedere la scrittura di un servizio dedicato. Un docker run permette di avere un editor funzionante in due minuti. Questa guida tratta il restante novanta per cento: renderlo persistente usando Postgres invece del file SQLite predefinito, renderlo raggiungibile tramite HTTPS e, aspetto che quasi tutti configurano in modo errato, fare in modo che i webhook forniscano un URL effettivamente raggiungibile dall'esterno.

Lo stack completo è costituito da due container sulla stessa rete Docker: n8n e un database Postgres che contiene i workflow e le credenziali. Un reverse proxy sull'host termina TLS e inoltra le richieste a n8n su localhost, quindi nessun servizio è esposto direttamente su Internet al di fuori di quel proxy. Si aggiunge agli altri servizi presenti nella shortlist del self-hosting 2026.

Prerequisiti e limiti reali

Serve un VPS con almeno 1 GB di RAM. Prevedi 2 GB quando i workflow iniziano a svolgere attività reali, perché le esecuzioni e il runtime Node.js consumano memoria. Se l’out-of-memory killer arresta il container durante un’esecuzione, te ne accorgi nel modo peggiore. Per iniziare è sufficiente 1 vCPU. Se il server eseguirà anche un servizio più pesante, dimensiona prima le risorse per quel servizio. Il caso più comune è una libreria fotografica: i requisiti minimi effettivi di RAM di PhotoPrism e Immich sono molto superiori a quelli richiesti da n8n. Lo stesso vale per un server multimediale. Un server Jellyfin e un’interfaccia web per consultarlo, come Halcyon, che ricostruisce la libreria come un videonoleggio degli anni 90, utilizzeranno RAM e capacità di transcodifica molto prima che n8n ne rilevi la necessità.

Serve un dominio o sottodominio, ad esempio n8n.example.com, con un record A che punti all'indirizzo IP pubblico del VPS e che sia già risolto prima di richiedere un certificato. Le porte 80 e 443 devono essere aperte verso il proxy. La porta 5678 utilizzata da n8n non deve essere esposta a Internet. Servono Docker Engine e il plugin Compose. Se docker compose version restituisce un errore con docker: 'compose' is not a docker command, significa che è installato il vecchio binario standalone e che il plugin è sudo apt install docker-compose-plugin.

SQLite va bene per i test, Postgres per tutto ciò da cui dipendi

Il database predefinito di n8n è un file SQLite in /home/node/.n8n/database.sqlite. Per fare prove va bene: non montare alcun volume e perderai il database al primo ricreare del container. Anche questa è una lezione utile. Il motivo per passare a Postgres non è la velocità pura. SQLite consente un solo lock di scrittura, quindi un'istanza che esegue più workflow contemporaneamente, oppure la modalità queue che prima o poi vorrai usare, genera SQLITE_BUSY: database is locked in condizioni di concorrenza. Postgres non ha questo limite, consente backup affidabili con pg_dump ed è il database previsto dalla documentazione di n8n per i server da cui dipendi. In seguito dovrai migrare manualmente i dati. Se questo server è importante, inizia con Postgres.

DNS e firewall

Punta prima il record e apri le porte, così il passaggio successivo relativo al certificato non fallisce perché il nome non viene risolto.

dig +short n8n.example.com
curl -s ifconfig.me
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow OpenSSH
sudo ufw enable

Non aprire 5678. Il file Compose associa n8n a 127.0.0.1:5678, quindi solo il reverse proxy dell'host può raggiungerlo; un ufw allow 5678 annullerebbe questo isolamento.

Il file Compose

Crea una directory di lavoro e un file docker-compose.yml. Questo definisce l'intero stack: due servizi, una rete privata e due volumi denominati.

services:
  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: n8n
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - n8n_net
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U n8n -d n8n"]
      interval: 10s
      timeout: 5s
      retries: 5

  n8n:
    image: docker.n8n.io/n8nio/n8n:2.29.10
    restart: unless-stopped
    ports:
      - "127.0.0.1:5678:5678"
    environment:
      - N8N_HOST=n8n.example.com
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://n8n.example.com/
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
      - N8N_PROXY_HOPS=1
      - GENERIC_TIMEZONE=Europe/London
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
    volumes:
      - n8n_data:/home/node/.n8n
    networks:
      - n8n_net
    depends_on:
      postgres:
        condition: service_healthy

volumes:
  postgres_data:
  n8n_data:

networks:
  n8n_net:

È importante chiarire alcune scelte. DB_POSTGRESDB_HOST=postgres è il nome del servizio che Docker risolve sulla rete condivisa, non localhost, che all'interno del container n8n indica n8n stesso. depends_on con condition: service_healthy impedisce a n8n di avviarsi prima di Postgres; senza questa impostazione, n8n si avvia, non trova il database e termina. Il volume denominato n8n_data montato in /home/node/.n8n contiene la chiave di crittografia e, con SQLite, il database: è l'unica directory che non devi perdere. Fissa l'immagine a una versione esatta, mai latest; i motivi sono descritti nella sezione sugli aggiornamenti qui sotto.

Il file dei secret

Non inserire mai le password nel file Compose. Inseriscile in un file .env accanto a questo file, che Compose legge automaticamente, e generane i valori in modo che siano realmente casuali.

printf 'POSTGRES_PASSWORD=%s\n'  "$(openssl rand -hex 24)" >  .env
printf 'N8N_ENCRYPTION_KEY=%s\n' "$(openssl rand -hex 32)" >> .env
chmod 600 .env

N8N_ENCRYPTION_KEY è la stringa più importante di questa configurazione: è la chiave con cui vengono cifrate tutte le credenziali memorizzate. Impostala esplicitamente invece di lasciare che la generi n8n, perché un valore generato da te può essere annotato e ripristinato. Dopo che n8n ha cifrato la prima credenziale con questa chiave, modificarla rende impossibile decifrare tutte le credenziali. Impostala quindi ora, una sola volta, e non modificare mai più quella riga.

Le variabili d'ambiente che determinano il funzionamento dei webhook

Quattro variabili controllano il modo in cui n8n si presenta all'esterno. Configurarle in modo errato è la causa principale delle richieste di supporto su n8n.

  • N8N_HOST è il nome host pubblico, n8n.example.com. Se lo si lascia al valore predefinito localhost dietro un proxy, l'editor prova a caricare la propria API da localhost nel browser, ma l'operazione non riesce.
  • N8N_PROTOCOL=https indica a n8n che il servizio è esposto tramite TLS. In questo modo n8n contrassegna il cookie di sessione Secure e genera URL https://.
  • N8N_PORT=5678 è la porta su cui n8n è in ascolto all'interno del container. Non è la porta pubblica: il proxy gestisce la porta 443.
  • WEBHOOK_URL=https://n8n.example.com/ è la variabile più problematica. n8n stampa gli indirizzi dei webhook da incollare in Stripe, GitHub o in qualsiasi client esterno generandoli a partire da questi valori. Se non è impostata o contiene un valore errato, n8n usa N8N_HOST:N8N_PORT e restituisce https://n8n.example.com:5678/webhook/... oppure, nel caso peggiore, http://localhost:5678/webhook/.... L'indirizzo viene stampato senza errori, sembra plausibile, ma non è raggiungibile da Internet. Di conseguenza, le richieste del client non arrivano mai e il problema resta silenzioso. Impostarla sull'URL di base pubblico esatto, con la barra finale, quindi verificare che il nodo webhook mostri un URL senza porta.

N8N_PROXY_HOPS=1 indica al server Express di n8n di considerare attendibile un proxy davanti a sé. In questo modo il rate limiting e tutte le funzioni che leggono l'indirizzo IP del client vedono l'indirizzo reale anziché quello del proxy. In questo caso una variabile da non impostare deliberatamente è N8N_RUNNERS_ENABLED: i task runner, cioè i processi separati e isolati in cui n8n esegue la logica dei nodi Code, sono il comportamento predefinito dalla versione 1.69 e sono obbligatori dalla linea 2.x indicata in questa guida. La vecchia modalità di attivazione esplicita è quindi deprecata. Se la si imposta ora, n8n registra soltanto un avviso che ne richiede la rimozione.

Primo avvio

docker compose up -d
docker compose ps
docker compose logs -f n8n

Un primo avvio riuscito termina con una riga Editor is now accessible via:, preceduta da una riga n8n ready on ..., port 5678. docker compose ps dovrebbe mostrare entrambi i container Up, con postgres contrassegnato come (healthy). Se n8n entra in un ciclo Restarting, consulta i log: nella maggior parte dei casi la causa è la connessione al database oppure i permessi sul volume descritti di seguito.

TLS con un reverse proxy

n8n comunica tramite HTTP in chiaro sulla porta 5678; un componente a monte termina la connessione HTTPS. Esistono due opzioni semplici.

Se esegui già diversi container, pubblica n8n tramite un reverse proxy Traefik che emette automaticamente i certificati TLS. Sono sufficienti alcune label: Traefik richiede e rinnova il certificato automaticamente.

Se questo è l'unico servizio sul server, un virtual host nginx con un certificato Let's Encrypt è più semplice. Usa la configurazione TLS di Certbot e nginx per Ubuntu 24.04 per ottenere il certificato, quindi utilizza questo server block:

server {
    listen 443 ssl;
    server_name n8n.example.com;

    ssl_certificate     /etc/letsencrypt/live/n8n.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/n8n.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:5678;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 3600;
        client_max_body_size 16m;
    }
}

Gli header Upgrade e Connection "upgrade" sono obbligatori. n8n invia gli aggiornamenti in tempo reale delle esecuzioni all'editor tramite WebSocket. Senza queste due righe, la pagina di accesso viene caricata ma poi si blocca mostrando un avviso di connessione persa. proxy_read_timeout 3600 impedisce che le esecuzioni di lunga durata vengano interrotte dopo i 60 secondi predefiniti di nginx. L'header X-Forwarded-Proto $scheme completa N8N_PROXY_HOPS=1: informa n8n che la richiesta originale era HTTPS, anche se il proxy lo raggiunge tramite HTTP in chiaro. In questo modo n8n non considera la connessione non sicura e non rifiuta il proprio cookie.

Il primo workflow, per renderlo operativo

Apri https://n8n.example.com/, crea l'account proprietario (nella sezione successiva) e costruisci il workflow più semplice che dimostri il corretto funzionamento del percorso: webhook in ingresso, chiamata HTTP e risposta in uscita.

  1. Aggiungi un nodo Webhook. Imposta il metodo su POST e un percorso come hello. Il nodo mostra due URL, un Test URL e un Production URL: da qui deriva una parte significativa delle segnalazioni secondo cui “il mio webhook non funziona”. Il Test URL risponde a una sola chiamata e soltanto dopo che hai fatto clic su Listen for test event; in seguito scade. Il Production URL risponde quando il workflow è Active.
  2. Aggiungi un nodo HTTP Request dopo il webhook e configuralo per usare una qualsiasi API JSON pubblica. Una richiesta GET a https://api.github.com/zen restituisce una stringa su una sola riga, ed è sufficiente.
  3. Aggiungi un nodo Respond to Webhook e imposta l'opzione Respond del nodo Webhook su "Using Respond to Webhook node", in modo che il chiamante riceva in risposta l'output del nodo HTTP.
  4. Imposta il workflow su Active (in alto a destra) ed esegui una chiamata a curl -X POST https://n8n.example.com/webhook/hello. Dovresti ricevere la frase zen: richiesta POST in ingresso, chiamata API e risposta in uscita. Questa è la struttura della maggior parte delle automazioni reali.

Una variante pianificata sostituisce il nodo Webhook con un Schedule Trigger e chiama invece un endpoint di un modello. Un'istanza self-hosted di Ollama in esecuzione sullo stesso VPS è un modo ordinato per creare un riepilogatore notturno.

Gestione degli utenti, non autenticazione di base

Le guide meno recenti di n8n indicano di impostare N8N_BASIC_AUTH_ACTIVE=true. Queste variabili sono state rimosse in n8n 1.0 e ora non hanno alcun effetto. Oggi l’autenticazione usa l'account proprietario: al primo accesso all'editor, n8n richiede la creazione di un account proprietario con email e password. Questo controllo è obbligatorio e non esiste una modalità anonima. Crealo subito dopo il primo avvio, prima di condividere l'URL: tra docker compose up e l'invio del primo modulo, l'istanza può essere acquisita dal primo utente che riesce a raggiungerla. Un livello di basic auth sul reverse proxy è una misura aggiuntiva ragionevole, ma costituisce un secondo fattore e non l'autenticazione effettiva. L'account proprietario e tutte le altre funzionalità descritte in questa guida sono disponibili nella Community Edition gratuita. Se in seguito vuoi aggiungere altri utenti con ruoli granulari o il supporto SSO, vale la pena leggere quali funzionalità di n8n richiedono una licenza a pagamento prima di pianificare l'installazione in base a queste esigenze.

Backup: prima la chiave di crittografia, poi il database

È necessario eseguire il backup di due elementi, ma non sono sostituibili allo stesso modo.

La N8N_ENCRYPTION_KEY. Ogni credenziale memorizzata in n8n, inclusi token API, password del database e secret OAuth, viene crittografata a riposo con questa chiave. I workflow presenti in Postgres sono inutilizzabili senza di essa: se ripristini il database su un nuovo server con una chiave diversa, n8n non può decrittografare neppure una credenziale. Non è possibile recuperarle né reimpostarle. Il file .env contiene la chiave. Copialo fuori dal server, idealmente in una voce del password manager, il giorno stesso in cui crei la chiave. Questo è il backup realmente importante.

Il database Postgres, che contiene i workflow, la cronologia delle esecuzioni e le credenziali crittografate:

docker compose exec -T postgres pg_dump -U n8n -d n8n \
  | gzip > n8n-db-$(date +%F).sql.gz

Esegui questo comando secondo una pianificazione e copia il dump fuori dal server. Per ripristinarlo su un VPS nuovo: avvia lo stack una volta, in modo che il database venga creato, arresta n8n, ricarica il dump con psql, inserisci lo stesso N8N_ENCRYPTION_KEY in .env e avvia n8n. La stessa chiave più il dump producono un'istanza funzionante; una chiave nuova rende inutilizzabili i workflow per qualsiasi credenziale.

Aggiornamenti: bloccare il tag

Il file Compose blocca n8nio/n8n:2.29.10 anziché latest intenzionalmente. n8n rilascia una nuova versione minor quasi ogni settimana e, occasionalmente, modifica lo schema del database o il comportamento dei nodi tra una versione e l’altra. Per questo, latest implica che un pull non presidiato possa fornirvi una build che migra il database appena viene avviata. Bloccate una versione, leggete le note di rilascio prima di aggiornarla: n8n indica lì le modifiche incompatibili. Eseguite quindi l’aggiornamento in modo controllato:

docker compose exec -T postgres pg_dump -U n8n -d n8n | gzip > pre-upgrade.sql.gz
# edit the image tag in docker-compose.yml, then:
docker compose pull n8n
docker compose up -d n8n
docker compose logs -f n8n

I passaggi tra versioni major sono quelli in cui questo approccio è più importante. La linea 2.0, ad esempio, ha impostato N8N_BLOCK_ENV_ACCESS_IN_NODE su true come valore predefinito. Di conseguenza, qualsiasi nodo Code che leggeva process.env perde l’accesso senza generare un errore evidente, finché non lo reimpostate su false. La stessa release ha inoltre iniziato ad applicare permessi restrittivi al file delle impostazioni. Leggete la pagina sulle modifiche incompatibili della versione 2.0 prima di passare a una nuova versione major. n8n esegue automaticamente all’avvio le migrazioni del database necessarie. Proprio per questo, il pg_dump precedente all’aggiornamento non è facoltativo. Poiché le credenziali sono archiviate in forma cifrata usando una chiave in .env e i dati risiedono in Postgres, i container possono essere ricreati: per aggiornare, sostituiteli; per eseguire il rollback, bloccate il tag precedente e ripristinate il dump.

Modalità di errore e stringhe visualizzate

The requested webhook "POST hello" is not registered. Un errore 404 si verifica quando richiami un webhook il cui workflow non è Active oppure quando richiami il percorso di test mentre non è in ascolto alcun listener. I percorsi di test (/webhook-test/...) rispondono solo dopo aver fatto clic su "Listen for test event"; i percorsi di produzione (/webhook/...) rispondono solo quando l'interruttore del workflow è attivo. Il codice gemello This webhook is not registered for GET requests. Did you mean to make a POST request? indica invece che il metodo è errato: il nodo prevede POST, ma hai inviato GET.

L'URL del webhook mostra :5678 o localhost. Il nodo visualizza https://n8n.example.com:5678/webhook/... o http://localhost:5678/.... WEBHOOK_URL non è impostata oppure è errata, quindi n8n ha creato l'indirizzo usando N8N_HOST:N8N_PORT invece della base pubblica. Imposta WEBHOOK_URL=https://n8n.example.com/, ricrea il container con docker compose up -d e la porta non verrà più visualizzata.

There was a problem loading init data nel browser. L'editor è stato caricato, ma non riesce a raggiungere la propria API backend. Dietro un proxy, nella quasi totalità dei casi la causa è un valore errato di N8N_HOST o WEBHOOK_URL, l'assenza degli header Upgrade per WebSocket nel proxy oppure un valore di N8N_PROTOCOL non coerente con il metodo di connessione. Verifica le quattro variabili esposte pubblicamente e che il proxy inoltri Upgrade e Connection.

password authentication failed for user "n8n" nei log, mentre il container viene riavviato. La password inviata da n8n non corrisponde a quella con cui il database è stato inizializzato. Il punto critico è questo: Postgres legge POSTGRES_PASSWORD solo quando inizializza una directory dati vuota. Avvia lo stack una volta, poi modifica POSTGRES_PASSWORD in .env: il volume postgres_data esistente continuerà a contenere la vecchia password. Ripristina la password originale oppure, se non devi conservare alcun dato, esegui docker compose down e docker volume rm sul volume di postgres, quindi avvia nuovamente lo stack da zero.

EACCES: permission denied, open '/home/node/.n8n/config' all'avvio. n8n viene eseguito come utente node (UID 1000) e non può scrivere nella propria directory di configurazione. Il problema si presenta spesso quando si monta una directory dell'host (./n8n_data:/home/node/.n8n) di proprietà di root. Usa il volume denominato mostrato sopra oppure, se vuoi usare un bind mount, esegui prima sudo chown -R 1000:1000 ./n8n_data.

Permissions 0644 for n8n settings file /home/node/.n8n/config are too wide. Changing permissions to 0600.. Dalla serie 2.x n8n applica per impostazione predefinita 0600 a quel file di configurazione e corregge automaticamente i permessi durante l'avvio. Questa riga di log indica che la modalità era già stata corretta, in genere dopo un bind mount oppure dopo un ripristino che aveva ricopiato il file con permessi troppo permissivi. Non è necessario intervenire; imposta N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=false solo se il filesystem non supporta realmente i permessi.

Mismatching encryption keys; la riga completa indica che la chiave di crittografia nel file di configurazione /home/node/.n8n/config non corrisponde a N8N_ENCRYPTION_KEY nell'ambiente. La chiave nell'ambiente è diversa da quella scritta da n8n nel volume dati durante un'esecuzione precedente. La causa più comune è che n8n abbia generato una chiave casuale al primo avvio, quando la variabile non era impostata, e che in seguito tu ne abbia configurata una diversa. Ripristina la chiave originale in .env oppure, solo se non devi conservare credenziali memorizzate, elimina il file config all'interno del volume n8n_data e lascia che n8n lo rigeneri. Le credenziali esistenti diventeranno illeggibili.

Un avviso di accesso relativo ai cookie sicuri: Your n8n server is configured to use a secure cookie, however you are either visiting this via an insecure URL, or using Safari. Hai impostato N8N_PROTOCOL=https, ma hai raggiunto n8n tramite HTTP semplice, in genere collegandoti direttamente all'IP e alla porta invece di usare il proxy HTTPS. Accedi tramite https://n8n.example.com/. Imposta N8N_SECURE_COOKIE=false solo se non puoi realmente usare HTTPS e mai su un server esposto a Internet.

Per inserire un modello linguistico in questi workflow, consulta creare workflow di intelligenza artificiale con Claude e n8n.

FAQ

Devo usare SQLite o Postgres per n8n?

SQLite (il valore predefinito) è sufficiente per provare n8n e per un'istanza personale che esegue un workflow alla volta. Passa a Postgres per qualsiasi ambiente da cui dipendi: il lock del singolo writer di SQLite genera database is locked in condizioni di concorrenza, mentre Postgres consente backup affidabili con pg_dump. La migrazione successiva è manuale, quindi, se il server è importante, inizia con Postgres.

Perché i webhook di n8n non vengono mai attivati?

Quasi sempre la causa è WEBHOOK_URL. Se non è impostata o è errata, n8n stampa indirizzi webhook costruiti a partire da N8N_HOST:N8N_PORT, spesso contenenti :5678 o localhost, che sembrano validi ma non sono raggiungibili da Internet. Di conseguenza, le richieste del chiamante non arrivano mai. Imposta WEBHOOK_URL=https://n8n.example.com/ e verifica che il nodo mostri un URL senza porta. La seconda causa è chiamare un webhook il cui workflow non è stato impostato su Active; in questo caso viene restituito The requested webhook ... is not registered.

Che cosa devo includere nei backup di n8n?

Due elementi. Il N8N_ENCRYPTION_KEY del file .env, perché ogni credenziale memorizzata viene crittografata con questo valore e, se lo perdi, non può più essere decrittografata. Copialo fuori dal server il giorno stesso in cui lo crei. E un pg_dump del database Postgres che contiene workflow, cronologia e credenziali. Per un ripristino servono entrambi: la stessa chiave e il dump.

Come posso mettere n8n dietro HTTPS?

n8n espone HTTP non crittografato sulla porta 5678; un reverse proxy in primo piano termina TLS. Associa n8n a 127.0.0.1:5678, in modo che solo il proxy possa raggiungerlo, quindi usa Traefik con certificati automatici oppure nginx con un certificato Let's Encrypt. Imposta N8N_PROTOCOL=https e WEBHOOK_URL=https://your-host/ e verifica che il proxy inoltri gli header Upgrade del WebSocket; in caso contrario l'editor si blocca.

Come posso aggiornare n8n in sicurezza?

Blocca un tag specifico dell'immagine invece di latest, crea prima un pg_dump perché n8n esegue automaticamente le migrazioni all'avvio, consulta le note di rilascio per verificare eventuali modifiche incompatibili, quindi aggiorna il tag ed esegui docker compose pull n8n && docker compose up -d n8n. Il container è ricreabile, quindi puoi eseguire il rollback bloccando il tag precedente e ripristinando il dump creato prima dell'aggiornamento.