Cosa fa#
Docker crea reti virtuali isolate tra i container. Ogni rete e' come un palazzo separato — i container dentro si vedono tra loro, quelli fuori non esistono. Il DNS interno risolve i nomi dei container automaticamente.
TL;DR#
# Senza reti esplicite: ogni compose crea la sua rete isolata
# I container di compose diversi non si parlano
# Con reti condivise: i container si trovano per nome
n8n ────► postgres (stessa rete "internal")
└── n8n raggiunge postgres con hostname "postgres"
# Traefik vede solo i container sulla rete "web"
browser ──► traefik ──► app (rete web)
NON vede postgres (solo rete internal)Le reti Docker replicano la segmentazione di rete: ogni servizio e' esposto solo a chi deve vederlo.
Analogia — il condominio con citofoni#
Condominio A (rete internal): Condominio B (rete web):
postgres ←→ redis traefik ←→ rails
rails ←→ postgres
rails sta in ENTRAMBI i condomini — ha due citofoni.
postgres sta solo nel condominio A — dall'esterno non lo raggiungi.Tipi di rete#
graph TD
subgraph Host fisico
subgraph BridgeDefault["bridge default - docker0 - 172.17.0.x"]
A[Container A]
B[Container B]
A <-->|IP fisso| B
end
subgraph BridgeCustom["bridge custom - mynetwork"]
C[Traefik]
D[Chatwoot]
C <-->|nome container| D
end
subgraph HostNet["host - nessun isolamento"]
E[Container E]
end
BridgeDefault -->|NAT| eth0
BridgeCustom -->|porta esposta| eth0
HostNet -->|accesso diretto| eth0
end
eth0 --> Internet
| Tipo | Isolamento | DNS interno | Quando usare |
|---|---|---|---|
bridge default | Parziale | No — solo IP | Test rapidi |
bridge custom | Completo | Si — per nome | Produzione |
host | Nessuno | N/A | Performance massima |
none | Totale | N/A | Container completamente isolato |
DNS interno Docker#
Nelle reti bridge custom, Docker avvia un DNS interno. I container si raggiungono per nome — non per IP:
# Rete default — devi conoscere l'IP (cambia ad ogni riavvio)
ping 172.17.0.3 # fragile
# Rete custom — usi il nome del servizio docker-compose
ping postgres # stabile, sempre funziona
ping redis
ping chatwootQuesto e' il motivo per cui nel docker-compose si scrive:
# .env
DATABASE_URL=postgresql://user:pass@postgres:5432/chatwoot
# ^^^^^^^^
# nome del container — non IP
REDIS_URL=redis://redis:6379Owned vs External#
Ogni rete ha un compose che la possiede (la crea) e altri che la usano.
# Chi CREA la rete (proprietario)
# traefik/docker-compose.yml
networks:
web:
name: web # nome fisico fisso su Docker
# Chi USA la rete (tutti gli altri)
# n8n/docker-compose.yml
networks:
web:
external: true # "non crearla, cercala gia esistente"
name: webIl proprietario naturale di una rete e' il servizio che:
- vive piu' a lungo degli altri
- e' il primo ad avviarsi
- e' l'ultimo a spegnersi
Nome fisso con name:#
Senza name:, Docker usa il prefisso della cartella del compose:
cartella: infra/traefik/
rete dichiarata: web
risultato: traefik_web ← dipende dalla cartellaCon name: web il nome e' sempre web, indipendentemente da dove sta il compose.
Cosa succede se la rete non esiste#
Se un compose dichiara external: true e la rete non esiste:
docker compose up
ERROR: Network "web" declared as external, but could not be found.Se si tenta di eliminare una rete ancora in uso:
docker compose down # su traefik
Network web Resource is still in useDocker si rifiuta — comportamento di protezione corretto.
L'ordine di avvio e' critico: il compose proprietario della rete deve partire per primo. Chi usa external: true deve partire dopo.
Ordine di avvio#
Con reti external: true, l'ordine in cui avvii i compose e' obbligatorio:
# 1. Prima il proprietario della rete — la crea
docker compose up -d # traefik — crea la rete "web"
# 2. Poi chi usa la rete
docker compose up -d # app — usa "web" con external: trueSe avvii nell'ordine sbagliato:
ERROR: Network "web" declared as external, but could not be found.Il problema piu' comune e' avviare i compose nell'ordine sbagliato al reboot del server. Soluzione: script di avvio ordinato, o depends_on se i servizi sono nello stesso compose.
Esempio reale — due reti con Traefik#
graph TD
Internet -->|443| T[Traefik]
subgraph web["Rete web - condivisa con Traefik"]
T -->|HTTP interno| App[app :3000]
end
subgraph internal["Rete internal - isolata"]
App <-->|SQL| PG[postgres :5432]
App <-->|cache| Redis[redis :6379]
end
# docker-compose.yml
services:
postgres:
networks:
- internal # solo rete interna — non raggiungibile da Traefik
app:
networks:
- internal # parla con postgres e redis
- web # parla con Traefik
networks:
internal:
driver: bridge
web:
external: true # esiste gia' — creata da TraefikCon external: true si fa riferimento a una rete gia' esistente, creata da un altro compose.
Porte — esporre vs non esporre#
# EXPOSE nel Dockerfile — solo documentazione, non apre nulla
EXPOSE 3000
# ports in docker-compose — apre la porta sull'host
ports:
- "3000:3000" # 0.0.0.0:3000 → tutti gli IP dell'host
- "127.0.0.1:3000:3000" # solo localhost → non raggiungibile da fuoriRete Docker chiamata web e porta esposta su internet sono due cose diverse:
- Rete
web= canale virtuale tra container, NON esposta su internet 0.0.0.0:443:443= porta esposta su internet127.0.0.1:5432:5432= solo localhost, non su internet
Traefik — exposedByDefault e label#
Con --providers.docker.exposedbydefault=false, Traefik non espone automaticamente tutti i container sulla rete web. Un container e' visibile solo con il label esplicito:
labels:
- "traefik.enable=true"Se il label non c'e', Traefik ignora il container anche se e' sulla stessa rete.
Comandi essenziali#
| Comando | Cosa fa |
|---|---|
docker network-defense ls | Lista tutte le reti |
docker network-defense inspect web | Dettagli: IP, container connessi, subnet, driver |
docker network-defense create mynetwork | Crea rete custom |
docker network-defense connect mynetwork C1 | Aggiunge container a rete |
docker network-defense disconnect web C1 | Rimuove container da rete |
docker network-defense prune | Elimina reti non usate da nessun container |
docker ps -a --filter network=web | Trova tutti i container su una rete specifica |
Un container senza rete esplicita finisce sulla rete bridge default — dove tutti i container si vedono per IP. In produzione definisci sempre reti custom esplicite.
Per verificare che due container non si parlino (isolamento corretto): docker exec container_A ping container_B — se il ping fallisce, l'isolamento funziona.
Scenario Reale#
Un database non deve mai essere raggiungibile da Traefik o da internet. Si mette su una rete internal senza external: true in Traefik. Solo il container applicativo, che ha bisogno di entrambe le reti, fa da tramite tra web e internal.
Note personali#
Il problema piu' comune e' avviare i compose nell'ordine sbagliato. Se un compose usa external: true e il proprietario non e' ancora partito, il compose fallisce immediatamente con external network-defense not found.
La rete internal non deve mai avere container esposti su web — se un DB finisce su web e' un errore di configurazione, non un comportamento intenzionale.
Collegato a#
- system — categoria
- docker-compose — dove si definiscono le reti
- docker-security — isolamento come difesa
- docker-volumes — stesso pattern owned/external applicato ai volumi
- reverse-proxy — Traefik vive nella rete web


