Exposing a Node.js App Over HTTP / HTTPS on a Subdomain in DDEV

April 10, 2025 3 min read

Edit this page
Screenshot of DDEV router and Traefik running on a `.ddev.site` subdomain in a browser window

Exposing a Node.js App Over 80/443 on a Subdomain in DDEV

Have you ever needed to run a separate Node.js application alongside your main site in DDEV, and serve it securely over ports 80 and 443 on a custom subdomain? While DDEV has built-in mechanisms for exposing additional ports, sometimes you need more control, especially if you want to expos it through a domain like frontend.example.ddev.site instead of a port-specific URL. This is where Traefik, bundled with DDEV, becomes incredibly powerful.

In this post, we’ll walk through how to configure DDEV and Traefik to proxy requests from a subdomain directly to your Node.js app on port 3000 within the DDEV web container.


Why Not Just Use web_extra_exposed_ports?

DDEV’s’ web_extra_exposed_ports feature is great for making your service accessible via a specific port (e.g., (3000)). However, it doesn’t magically set up a subdomain for you to use on standard web ports (80/443). If you want frontend.example.ddev.site to map to your Node.js app over HTTPS, you need a reverse proxy rule. That’s where Traefik comes in.

Step 1: Update Your .ddev/config.yaml

In your project’s .ddev/config.yaml, define the project name and the additional hostname you want to use. For example:

name: example

additional_hostnames:
  - frontend.example

(Optional) You can still use web_extra_exposed_ports to expose the Node.js port if you want:

web_extra_exposed_ports:
  - name: node-app
    container_port: 3000
    http_port: 3000
    https_port: 3001

However, for a subdomain over standard web ports, the critical part is the next step with Traefik.

Step 2: Create a Project-level Traefik Configuration File

In your project’s .ddev/traefik/config folder add a file named frontend.yaml. In frontend.yaml, you’ll define two routers—one for HTTP (port 80) and one for HTTPS (port 443)—and a service that points to the Node.js app on port 3000.

http:
  routers:
    # Router for HTTP (port 80)
    example-web-80-http-frontend:
      entrypoints:
        - http-80
      rule: Host(`frontend.example.ddev.site`)
      service: "example-web-3000"
      ruleSyntax: v3
      tls: false
      priority: 100

    # Router for HTTPS (port 443)
    example-web-80-https-frontend:
      entrypoints:
        - http-443
      rule: Host(`frontend.example.ddev.site`)
      service: "example-web-3000"
      ruleSyntax: v3
      tls: true
      priority: 100

  services:
    # The custom service that routes to your Node app
    example-web-3000:
      loadbalancer:
        servers:
          - url: http://ddev-example-web:3000

Here’s what’s happening:

  • Routers: Each router inspects incoming requests. If the hostname matches frontend.example.ddev.site, it passes the request to the example-web-3000 service.
  • Service: Defines where to actually send the traffic. In this case, http://ddev-example-web:3000 is the internal address of the web container running on port 3000.

Step 3: Restart DDEV

Run:

ddev restart

DDEV will pick up your new Traefik configuration, and you should now be able to access your Node.js application at:

ddev launch https://frontend.example.ddev.site

No more messing with non-standard port numbers in your URLs!


Wrapping Up

By leveraging Traefik’s routing capabilities, you can expose any service running in the web container on standard HTTP/HTTPS ports and map it to a dedicated subdomain. This approach keeps your development environment clean, user-friendly, and closer to production-like URLs.

If you’ve followed these steps, your Node.js application will be served seamlessly over frontend.example.ddev.site.


Further Reading

Do You Have a Favorite DDEV Recipe? Contribute It!

We welcome community contributions to the DDEV blog and would love to have yours. The ddev.com repository has full details, and there’s even a training session on how to do it. It’s all just Markdown and we’ll help!

Posted In