Node.js Meet Lando: A Local Dev Power Combo

As a primarily Drupal-focused developer, I found myself completely in love with "Local Development Environment Orchestrators" such as Lando and DDEV. The extreme appeal of these tools for me comes from their simplicity (with some exceptions on Windows) and the consistency in how they function. I adore avoiding the messy process of installing binaries for different web application runtimes on my machine.

Have you ever tried installing PHP on Windows!? There's no installer! It's absolutely awful! Whenever I install PHP manually on Windows, I find myself worrying about how to maintain and update it. It just feels so very wrong to install something without a clear installer or update tool. Not to mention how complex it is to install multiple versions. Managing different versions of web application runtimes also adds unnecessary overhead. While there are solutions for managing multiple runtime versions—like Node Version Manager (NVM) for Node.js, which can even set versions per project—it all still feels messy to me. 

It’s much easier in Linux, where you can use your favorite package manager (mine’s apt) to install and maintain different web application runtimes. One might say “Windows is the problem”—and they wouldn’t be wrong. Windows bashing aside, if you're working on multiple projects that use different versions, things can get unbearably unruly.

On a team project, development overhead is greatly reduced when everyone uses the same runtime environment—regardless of their IDE.

Okay, so what about virtual machines and VM orchestrators like Vagrant? I’ve used that setup before. Vagrant isn’t terrible—it sets up your environment and gets you ready to develop. But VMs are heavy, resource-intensive, and don’t perform as well—especially on lower-end systems. Mounting file systems from the host into VMs can also suffer from performance issues—especially on Windows. Some users even go so far as to set up a full secondary desktop environment inside the VM to avoid latency issues—essentially treating it like a full web server, which is incredibly time-consuming. Wouldn’t it be great if you didn’t need to do “operating system inception” and things just worked—with almost no effort?

Docker-based local development environment orchestrators are the answer to this problem. They produce environments that are lightweight and ready to use. They allow teams to share local development environment configurations, which can greatly reduce troubleshooting time and ensure development stays aligned with the target production environment. No complex server configuration required.

Since PHP has been the bread and butter of my development career, I’ve definitely grown accustomed to using local development environment orchestrators. When working with Drupal—especially across multiple projects—these tools are by far the best and fastest way to start local development.

I imagine tools like NVM are why the Node.js community hasn’t widely embraced this kind of tooling—but versioning is only part of the picture. What about databases? Do Node.js developers go through the same messy process to install and manage them locally?

I occasionally dabble in Node.js development—mostly before I started using local development environment orchestrators. Coming back to Node.js, I was hoping for a similar experience to what I was used to when developing in PHP. Both Lando and DDEV support Node.js, but they don’t have specific recipes for working exclusively with Node.js or Express. That makes using these tools with Node.js-only projects a bit more complicated.

In the interest of helping my future self quickly spin up a functional Node.js project in Lando using Express, here’s a full .lando.yml to get a local dev environment running—with MongoDB, npm and nodemon aliasing, and port 3000 redirected to 80.

name: myapp
proxy:
  appserver:
    - myapp.lndo.site:3000

services:
  database:
    type: mongo:7.0

  appserver:
    type: node:22
    ssl: false

    port: 3000
    scanner:
      okCodes:
        - 502 # Ignores errors displayed that occur before the application is started.

tooling:
  npm:
    service: appserver
    description: Node Package Manager wrapper.
    cmd:
      - npm

  nodemon:
    service: appserver
    description: Starts Nodemon
    cmd:
      - nodemon

Ensuring Express Will Respond to Requests From the Proxy

Make sure that your app.listen() function includes the IP address 0.0.0.0 not including it will restrict the application to listening for connections via localhost instead of anything.

app.listen(3000, '0.0.0.0', () => console.log('Myapp listening on port 3000'));