The Container Networking Model: How Deployxa's Containers Communicate | Deployxa

Ever wondered how your frontend, backend, and database containers talk to each other on Deployxa? Here is the container networking model explained.

← Back to Dispatch Articles
Engineering Log

The Container Networking Model: How Deployxa's Containers Communicate

Ever wondered how your frontend, backend, and database containers talk to each other on Deployxa? Here is the container networking model explained.

The Container Networking Model

When you deploy a full-stack app on Deployxa (e.g., a Next.js frontend, a FastAPI backend, and a Postgres database), the containers need to communicate with each other. The frontend needs to call the backend's API, the backend needs to query the database, and all of them need to be accessible from the internet. How does this work? What is the networking model that enables container-to-container communication, container-to-internet communication, and internet-to-container communication? Here is the container networking model explained.

The direct answer is that Deployxa uses a combination of Docker networking (for container-to-container communication), Traefik v3 (for internet-to-container communication), and Cloudflare (for DDoS protection and CDN-like caching). Each app gets a Docker network, and all containers in the app (frontend, backend, database) are connected to the same network, which means they can communicate with each other via their container names. Traefik v3 routes traffic from the internet to the appropriate container, based on the hostname. Cloudflare sits in front of Traefik, providing DDoS protection and caching. For more on the infrastructure, see our article on Traefik v3 dynamic routing.

The Networking Layers

Deployxa's container networking has three layers:

1. The Docker network (container-to-container)

Each app gets a Docker network (e.g., my-app-network). All containers in the app (frontend, backend, database) are connected to the same network, which means they can communicate with each other via their container names. For example, the frontend container can call the backend container at http://my-app-backend:3000, and the backend container can connect to the database at my-app-db:5432. Docker's built-in DNS resolves the container names to their IP addresses, which means you do not need to hardcode IP addresses. For more on Docker networking, see the Docker documentation.

2. Traefik v3 (internet-to-container)

Traefik v3 is the edge proxy that routes traffic from the internet to the appropriate container, based on the hostname. When a user visits myapp.com, Traefik routes the request to the frontend container. When a user visits api.myapp.com, Traefik routes the request to the backend container. Traefik handles SSL termination (via Let's Encrypt), which means the connection between the user and Traefik is encrypted, and the connection between Traefik and the container is unencrypted (but on the same host, so it is secure). For more on Traefik, see our article on Traefik v3 dynamic routing.

3. Cloudflare (DDoS protection and CDN)

Cloudflare sits in front of Traefik, providing DDoS protection, CDN-like caching, and global routing. When a user visits myapp.com, the request first hits Cloudflare's edge network (which is global), which then proxies the request to Traefik (which is in a single region). Cloudflare caches static assets (e.g., images, CSS, JS) at the edge, which means users get fast response times for static content, regardless of their location. For dynamic content, Cloudflare proxies the request to Traefik, which routes it to the container.

Step-by-Step: How a Request Flows

Here is how a request flows from a user's browser to your frontend container, and then to your backend container, and then to your database.

Step 1: The user's browser sends a request

The user's browser sends a request to https://myapp.com/api/users. The request first hits Cloudflare's edge network (which is global), which proxies the request to Traefik (which is in a single region).

Step 2: Traefik routes the request to the frontend container

Traefik receives the request, matches the hostname (myapp.com) to the frontend container, and proxies the request to the frontend container. The frontend container is a Next.js app that has an API route at /api/users.

Step 3: The frontend container calls the backend container

The frontend container's API route needs to fetch data from the backend, so it makes a request to http://my-app-backend:3000/users. This request goes over the Docker network, which resolves my-app-backend to the backend container's IP address.

Step 4: The backend container queries the database

The backend container receives the request, queries the database at my-app-db:5432, and returns the result to the frontend container. The database query goes over the Docker network, which resolves my-app-db to the database container's IP address.

Step 5: The frontend container returns the response

The frontend container receives the data from the backend, renders the response, and returns it to Traefik. Traefik returns it to Cloudflare, and Cloudflare returns it to the user's browser.

Common Pitfalls and Troubleshooting

The first pitfall is CORS. If the frontend and backend are on different hostnames (e.g., myapp.com and api.myapp.com), the browser enforces CORS, which means the backend needs to allow CORS requests from the frontend. The fix is to configure CORS on the backend, as described in our article on the CORS trap. Alternatively, you can use a shared origin (e.g., myapp.com for the frontend and myapp.com/api for the backend), which eliminates CORS. The second pitfall is hardcoded URLs. If the frontend hardcodes the backend's URL (e.g., http://localhost:3000), the request will fail in production (because the backend is not on localhost). The fix is to use the Docker network's container name (e.g., http://my-app-backend:3000) or an environment variable. Deployxa's localhost rewriter handles the common case, as described in our article on the localhost trap. The third pitfall is the database connection. If the backend connects to the database via localhost, the connection will fail (because the database is in a separate container). The fix is to use the Docker network's container name (e.g., my-app-db:5432) or the DATABASE_URL environment variable. For more on database connections, see our article on fixing DATABASE_URL not set. The fourth pitfall is network isolation. By default, all containers in an app are on the same Docker network, which means they can communicate with each other. If you want to isolate a container (e.g., a database that should not be accessible from the internet), you can configure Traefik to not route traffic to it. The fifth pitfall is performance. Container-to-container communication over the Docker network is fast (it is on the same host), but if you have multiple hosts, the communication might go over the network, which is slower. The fix is to use Deployxa's single-region deployment (which keeps all containers on the same host) or to use a load balancer that routes traffic to the nearest host.

How the Networking Model Integrates with the MCP Server

The networking model is exposed via the Deployxa MCP server, which means your AI assistant can inspect and configure the networking. For example, you can ask "what is the backend's URL?" and the AI assistant will tell you http://my-app-backend:3000 (the Docker network URL). You can also ask "add a custom domain for the API" and the AI assistant will configure Traefik to route api.myapp.com to the backend container. For more on the MCP server, see our article on giving Cursor cloud superpowers.

Lessons Learned

Building the container networking model taught us several lessons. First, Docker networking is simple but powerful. Docker's built-in DNS and networking make container-to-container communication easy, without the need for custom configuration. Second, Traefik is a great edge proxy. Traefik's dynamic configuration (via Docker labels) makes it easy to route traffic to containers, without the need for configuration reloads. Third, Cloudflare is essential. Cloudflare's DDoS protection, caching, and global routing are essential for a production-grade platform, and they are free for basic usage. Fourth, CORS is a common pain point. Many developers struggle with CORS, which is why we built the localhost rewriter and the shared-origin pattern. Fifth, observability is critical. The networking model is complex (three layers, multiple containers, multiple hosts), which means it needs to be observable (e.g., network metrics, request tracing) so you can debug issues. For more on observability, see our articles on the metrics pipeline and the logging pipeline.

Advanced Networking Patterns

Beyond the basics, container networking benefits from several advanced patterns. The first is service mesh. A service mesh (e.g., Istio, Linkerd) provides advanced networking features (e.g., traffic splitting, circuit breaking, retries, timeouts) without code changes. For complex microservices architectures, a service mesh is valuable. For simpler apps, it is overkill. The second is network policies. Network policies (e.g., Kubernetes NetworkPolicy) let you control which containers can communicate with each other, which improves security. For apps with strict security requirements, network policies are essential. The third is private networks. For apps that need a private network (e.g., a database that should not be accessible from the internet), you can configure the container to be on a private network only, which means it is not exposed to the internet. The fourth is custom DNS. For apps that need custom DNS (e.g., myapp.internal for internal communication), you can configure custom DNS entries, which makes the URLs more readable. The fifth is load balancing. For apps with multiple containers, the load balancer (Traefik) distributes traffic across the containers, which improves performance and reliability. For more on networking, see our articles on Traefik v3 dynamic routing and the CORS trap.

When Custom Networking Is Not Needed

Custom networking is not always needed. For simple apps (a single container, a single domain), the default networking is sufficient, and custom networking adds complexity without providing significant benefit. For apps that do not need container-to-container communication (e.g., a static site), the Docker network is not needed. For apps that do not need a custom domain (e.g., a hobby project on a Deployxa subdomain), the default Traefik configuration is sufficient. For these apps, the default networking is fine. The key is to match the networking to the app's needs: for complex apps with multiple containers and custom domains, custom networking is valuable; for simple apps, the default is fine. For more on networking, see our articles on how we handle container isolation and the build cache architecture.

Conclusion: A Simple, Powerful Networking Model

Deployxa's container networking model is simple (Docker networking for container-to-container, Traefik for internet-to-container, Cloudflare for DDoS protection and CDN) but powerful (supports full-stack apps, polyglot monorepos, and custom domains). By understanding the model, you can debug networking issues, optimize performance, and build complex full-stack apps with confidence. For more on Deployxa's engineering, see our articles on Traefik v3 dynamic routing and the auto-scaling architecture. Learn about how we handle container isolation and the build cache architecture in our companion articles. Explore our free developer tools to speed up your workflow. Try Deployxa Drop for an instant live preview with zero signup.

Ready to deploy with Deployxa?

Deploy your apps globally with automatic SSL and AI diagnostics.

Start Free Now