Deploy an Express.js Server to Kubernetes
The easiest way to containerize and deploy an Express.js server to Kubernetes using DevSpace.

Introduction
In this tutorial, we are going to see how to easily deploy an Express.js server to Kubernetes using DevSpace. Here are the steps to do this:
- Create a simple Express.js app (take your own if you already have one)
- Add nodemon for hot reloading
- Containerize the server (Dockerfile & Helm Chart)
- Deploy our application to Kubernetes
- Stream the logs of your deployment
Prerequisites
Make sure that you have the following installed:
- Node
- npm
We will use DevSpace which is a development tool for Kubernetes. Run this command to install DevSpace:
npm install -g devspace
1. Create an Express.js Server
You can skip this step if you want to deploy your own Express.js server.
In case you want to start from scratch, let's create a basic Express.js server. We start by creating a minimal project structure and then installing Express:
mkdir express-server
cd express-server
# Create a package.json, -y flag to answer yes to all questions
npm init -y
# Install and save express as dependency
npm install --save express
Let's finish the creation of our basic Express.js server by creating a file called index.js
in the express-server directory:
const express = require('express');
const app = express();
app.get('/', function (req, res) {
res.send('Hello World!');
})
app.listen(3000, function () {
console.log('App listening on port 3000!');
})
As you can see, this simple Express.js server will be listening on port 3000. Now, we just need to tell npm
how it should start this express application when running npm start
. That's why we add start
to the scripts
section of our package.json
file which should look like this afterward:
{
"name": "express-server",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"express": "^4.17.1"
}
}
2. Containerize the Express.js Server
Our application requires a Dockerfile and some Kubernetes manifests in order to run on Kubernetes. This is when DevSpace comes into play: it will autogenerate these files for us, thanks to a simple command:
devspace init
Create a Dockerfile
DevSpace automatically detects that there is no Dockerfile in our project root directory and shows several options to continue with. Select the first option Create a Dockerfile for me
by hitting Enter
and DevSpace will create a Dockerfile in your project directory.
? This project does not have a Dockerfile. What do you want to do?
[Use arrows to move, space to select, type to filter]
> Create a Dockerfile for me
Enter path to your Dockerfile
Enter path to your Kubernetes manifests
Enter path to your Helm chart
Use existing image (e.g. from Docker Hub)
If there already is a Dockerfile in the project folder, DevSpace will detect it and suggest to use it instead of creating a new one.
Select The Programming Language
Our programming language will also be detected by DevSpace, so we can confirm that we are using javascript
by hitting Enter
.
? Select the programming language of this project
[Use arrows to move, space to select, type to filter]
csharp
go
java
> javascript
none
php
python
Choose an Image Registry
DevSpace is going to build a Docker image from our newly created Dockerfile. This image needs to be pushed somewhere. We get the following options:
? Which registry do you want to use for storing your Docker images?
[Use arrows to move, space to select, type to filter]
Use hub.docker.com
> Use dscr.io (free, private Docker registry)
Use other registry
If you have no clue which registry to use, you can just use dscr.io. It is free and allows you to push the images to a private registry. DevSpace will then open a login page and allow you to sign up for dscr.io.
Define The Application Port
Enter the port of the Express.js server (remember, we specified it via app.listen()
in index.js
). So in our case, we need to enter 3000
:
? Which port is your application listening on? (Enter to skip) 3000
Project Successfully Initialized
Once the init command has terminated, you will find the following new files in your project folder:
express-server/ # Your project directory
|
|--devspace.yaml # DevSpace config file (defines which images to build and how to deploy your project)
|--Dockerfile # Defines how to build the Docker image
|--.dockerignore # Defines paths to be ignored when building the Docker image
|--.gitignore # Defines paths to be ignored when commiting your code via git
Besides the configuration for DevSpace in devspace.yaml
, there is a Dockerfile
for building a Docker image for our Express.js server before deploying the app to Kubernetes.
Dockerfile
The autogenerated Dockerfile will look like this:
FROM node:8.11.4
# Create project directory (workdir)
RUN mkdir /app
WORKDIR /app
# Add package.json to WORKDIR and install dependencies
COPY package.json .
RUN npm install
# Add the remaining source code files to WORKDIR
COPY . .
# Start the application
CMD ["npm", "start"]
This Dockerfile:
- defines
node
as base image - creates the working directory
/app
- copies the
package.json
into the image and installs the dependencies - copies the rest of our application into the working directory
- defines
npm start
as the start command for the container
Adding the package.json
separately and installing dependencies before adding the rest of the application allows Docker to use layer-caching and saves a lot of time when building the image because the dependencies will only have to be re-installed when the package.json
has changed and not every time we change any other source code file. Not having to install the dependencies every time we re-build and re-deploy our application saves a lot of time.
Note that this Dockerfile starts our Express.js server in development mode. For production, it is recommended to also set the environment variable
NODE_ENV=production
which would be possible using a separate Dockerfile just for production.
Component Helm Chart
By default, DevSpace deploys your application as a so-called component that uses a highly configurable Helm chart. To customize the deployment of your app, you can edit the values
section of the deployments
section within the devspace.yaml
configuration file:
...
deployments:
- name: express-server
helm:
chart:
name: component-chart
version: v0.0.6
repo: https://charts.devspace.cloud
values:
containers:
- image: dscr.io/${DEVSPACE_USERNAME}/devspace
service:
ports:
- port: 3000
...
For more information on how to customize this component, take a look at customizing DevSpace components.
3. Deploy The Express.js Server
Now that our Express server is containerized, we are ready to deploy it to Kubernetes. DevSpace works with every Kubernetes cluster, no matter if it is running locally, in a public cloud or a private cloud. You can also use virtual Kubernetes clusters that are provided by loft.
The loft-devspace-plugin even allows you to create virtual Clusters with DevSpace. For this, you or your admin need to connect a cluster to loft first and then you install the loft-devspace-plugin with the command
devspace add plugin https://github.com/loft-sh/loft-devspace-plugin
. Afterwards, you and your teammates can create virtual clusters and isolated namespaces on a shared physical cluster.
When you have a Kubernetes cluster set up and the kube-context configured on your local machine, you can simply run this command:
devspace use namespace my-express-server
DevSpace will automatically create this namespace during the deployment process.
Build & Deploy Your App
DevSpace automates the entire deployment process (image building, pushing images to the registry, creating resources in Kubernetes etc.), so you can just run one single command to deploy the Express.js server:
devspace deploy
This command takes a little while when you run it for the very first time. If you run it later again, it will be much quicker.
Now, it's time to take a look at the deployment in the browser:
devspace open
When DevSpace asks you how to open your application, choose the first option: via localhost
? How do you want to open your application?
[Use arrows to move, space to select, type to filter]
> via localhost (provides private access only on your computer via port-forwarding) # <<<<<<<< CHOOSE THIS ONE!
via domain (makes your application publicly available via ingress)
4. Start Development
If we want to develop this application instead of just running it, we can add nodemon as hot reloading tool and use the file-synchronization of DevSpace to update the deployment within seconds.
Add nodemon for Hot Reloading
Our server is running nicely; nevertheless, if you tried to mess around and modify anything to the index.js
, you might have noticed that the changes are not applied, unless we run devspace deploy
again. This is a very tedious task that has a negative impact on our development workflow and efficiency, hence it needs to be fixed.
Luckily, there is a nice package that was created to overcome this problem: nodemon. Once installed, it will automatically restart our Node.js application when file changes are detected in the directory. So, let's install it:
npm install --save-dev nodemon
Instead of running node index.js
, we can now run:
nodemon index.js
Change The Start Command
Let's assume that you are in a small team, not everyone is supposed to know the specific command to use in order to start the app correctly. The best practice in this case is to add our nodemon index.js
command in the scripts of the package.json
:
"scripts": {
"start": "nodemon index.js"
},
This is only suitable for development. For production, you should add another script, e.g.
"start-prod": "node index.js"
.
Start Development Mode
You can now just run this command to develop directly inside the Kubernetes cluster:
devspace dev -b
In addition to deploying our Express.js server to Kubernetes, the devspace dev
command also starts a powerful development UI which runs on localhost and is available as long as the devspace dev
command is running.
You should see a log statement similar to this one in the output of devspace dev
:
#########################################################
[info] DevSpace UI available at: http://localhost:8090
#########################################################
So go ahead and check out the DevSpace UI in your browser! You can use it to see the container and pod status of your deployments as well as to stream logs and open interactive terminal sessions for every container. It should look like in this screencast:

Now, let's change something in our app to check if the hot reloading is working. For instance, replace Hello World!
with Bye World!
and then save the file. If it's working, a message similar to the one below should be displayed in the console:
[nodemon] restarting due to changes...
[nodemon] starting `node index.js`
App listening on port 3000!
Refresh your web page at http://localhost:3000/ and you should see your changes without having to restart the app manually!
Happy Coding!
I hope you liked this tutorial and were able to deploy your first Express.js app to Kubernetes. Leave a comment, clap for this post on Medium or share the link with your colleagues, if this article was helpful for you.