Docker Network Fix
cloudflared Stuck on the Wrong Container
The old setup had cloudflared tunneling to 172.18.0.1:8000, which was the WordPress container’s address. After deleting WordPress, that port vanished and cloudflared kept trying to hit a dead endpoint. The tunnel definition in /data/config/docker-compose.yml didn’t know about the new Astro container, and the port mapping was hard-coded to an IP that no longer existed. Any request to the tunnel would fail silently, returning 502 errors to visitors.
cloudflared:
image: cloudflare/cloudflared:latest
command: tunnel --no-autoupdate run --token ${CLOUDFLARE_TOKEN}
ports:
- "172.18.0.1:8000:8000"
networks:
- config_default
Watch out: Hard-coded IPs in Docker Compose port mappings are fragile. If the container restarts and gets a new IP (common with Docker’s default bridge network), the mapping breaks silently. Always use container names for service discovery when possible.
Why Docker Isolates Container Networks by Default
Docker creates an internal DNS resolver for each network. Containers can only resolve other containers that share the same network. In my case, cloudflared lived in config_default, while sovereign-blog lived in sovereign-blog_default. The tunnel couldn’t reach the Astro container by name because the networks were isolated.
$ docker exec -it cloudflared ping sovereign-blog
ping: sovereign-blog: Name or service not known
Even 127.0.0.1 wouldn’t work from inside a container because Docker binds ports to the host interface only. The host’s loopback isn’t shared between containers. This isolation is intentional—Docker’s default network model prioritizes security and predictability over convenience.
Gotcha: Docker’s internal DNS only resolves container names within the same network. If you try to ping a container by name from a different network, you’ll get
Name or service not known, even if the container exists. This is a common source of confusion when debugging multi-network setups.
Limitation: Docker’s default bridge network assigns IPs dynamically. If you rely on IPs (e.g.,
172.18.0.1:8000), your setup is brittle. Container restarts can change IPs, breaking hard-coded references. Use container names and Docker’s internal DNS instead.
Warning: Docker’s network isolation can cause unexpected behavior when mixing legacy setups (like my hard-coded IP) with modern service discovery. Always verify connectivity with
docker execandcurlbefore assuming a tunnel will work.
Attaching cloudflared to the Astro Network
Fixed by editing cloudflared’s compose file to join both networks. Added the external Astro network and kept the default for other services.
cloudflared:
image: cloudflare/cloudflared:latest
command: tunnel --no-autupdate run --token ${CLOUDFLARE_TOKEN}
networks:
- default
- sovereign-blog_default
networks:
default: {}
sovereign-blog_default:
external: true
Applied the change with:
sudo docker compose -f /data/config/docker-compose.yml up -d cloudflared
Version note: I’m using
cloudflare/cloudflared:latest(as of August 2024, this is2024.8.1). The--no-autoupdateflag prevents the container from self-updating, which is useful for stability but means you’ll need to manually update the image version when you want to upgrade.
Path note: The compose file is located at
/data/config/docker-compose.ymlon my host. Your path may differ depending on your Docker setup.
Error handling: If you see
network sovereign-blog_default not found, double-check the network name indocker network ls. Networks created by other compose files (e.g.,sovereign-blog_default) must be marked asexternal: truein your compose file.
The tunnel now resolves sovereign-blog via Docker’s internal DNS. The Astro container exposes port 4321, so the tunnel routes traffic there instead of the old WordPress port.
Updating the Cloudflare Zero Trust Tunnel
In the Cloudflare dashboard under Zero Trust → Networks → Tunnels → sparki, I removed the old route:
www.example.com → 172.18.0.1:8000 # WordPress, offline
Then added the new route:
www.example.com → http://sovereign-blog:4321
Cloudflare note: The tunnel name
sparkiis arbitrary—it’s just the name I gave to the tunnel in the Cloudflare dashboard. You can find your tunnel names under Zero Trust → Access → Tunnels.
DNS propagation: Cloudflare’s Zero Trust tunnels update almost instantly, but DNS changes (e.g., CNAME records) may take longer to propagate. If you’re switching domains, expect a delay of up to 24 hours for full propagation.
The tunnel picked up the change immediately. DNS resolution worked because cloudflared and sovereign-blog were now on the same network.
Debugging tip: If the tunnel doesn’t update, check the Cloudflare dashboard for errors. Common issues include:
- Incorrect tunnel token (check
${CLOUDFLARE_TOKEN}in your compose file).- Missing or misconfigured DNS records in Cloudflare’s dashboard.
- Firewall rules blocking traffic to the tunnel’s ingress IP.
The sovereign-blog Container Setup
The Astro container runs in its own compose file:
services:
sovereign-blog:
image: ghcr.io/your/repo:sovereign-blog:latest
ports:
- "127.0.0.1:4321:4321"
networks:
- sovereign-blog_default
environment:
- NODE_ENV=production
Image note: The Astro image is hosted on GitHub Container Registry (
ghcr.io). The:latesttag is convenient but risky for production. Pin to a specific version (e.g.,:v1.2.3) to avoid unexpected updates.
Port binding: The Astro container binds to
127.0.0.1:4321on the host, which restricts access to the local machine only. This is a security best practice, but it means cloudflared must resolve the container by name (sovereign-blog) within thesovereign-blog_defaultnetwork.
Error message: If you see
Error response from daemon: driver failed programming external connectivity, it usually means the port is already in use. Check for conflicts withsudo lsof -i :4321orsudo netstat -tulnp | grep 4321.
It binds to the host’s loopback on 4321, but the container name sovereign-blog is resolvable within the sovereign-blog_default network. cloudflared now tunnels directly to that name and port.
What’s Left to Do
The plan is to flip the switch for the webshop route once the Astro build is stable. Then update the Amazon Associates account to use the new domain. No more WordPress cruft, just a static site served through a tunnel that actually knows where to send traffic.
Future-proofing: Consider adding health checks to your Astro container (e.g.,
/healthzendpoint) to ensure the tunnel only routes traffic to healthy services. Cloudflare Zero Trust supports health-based routing, which can prevent downtime during deployments.
Monitoring gap: Currently, I don’t have alerts for tunnel failures. Set up Cloudflare’s built-in monitoring or integrate with a tool like Prometheus to track tunnel status and container health.
Security note: The
sovereign-blog_defaultnetwork is currently isolated, but if you add more services (e.g., a database), ensure they’re only accessible to trusted containers. Use Docker’s network policies or Cloudflare’s Zero Trust to restrict access further.
Performance tip: If you notice high latency, check Docker’s network performance. The default bridge network can introduce overhead. Consider using
macvlanorhostnetwork mode for high-throughput services, though this reduces isolation.
Backup plan: Always keep the old WordPress container around for a few days after migration. If something goes wrong with the Astro setup, you can quickly revert by updating the tunnel route back to the old container.
Documentation gap: I didn’t document the exact steps for migrating WordPress data to Astro. If you’re doing this yourself, plan for:
- Exporting WordPress content (use the WordPress export tool).
- Converting posts to Markdown (tools like
wordpress-export-to-markdowncan help).- Updating internal links and images to use the new domain.
Gotcha: Astro’s static site generator may not handle dynamic content (e.g., comments) out of the box. If you need comments, consider integrating a third-party service like Disqus or Commento.
Version check: Verify your Astro version with
astro --version. As of August 2024, the latest stable is4.10.2. Some plugins or themes may require specific versions, so pin your dependencies inpackage.json.
What I Actually Use
cloudflare/cloudflared:latest(v2024.8.1): Handles the Zero Trust tunnel without exposing the host to the internet.ghcr.io/your/repo:sovereign-blog:latest(v1.2.3): The Astro static site container serving the blog and shop.- Docker Compose v2.23.0: Keeps the networking clean and avoids manual port juggling.
Docker Network Fix
Resolving cloudflared tunnel routing to Astro container