Build a rock for an ExpressJS application¶
In this tutorial, we’ll containerise a simple ExpressJS application into a rock
using Rockcraft’s expressjs-framework
extension.
It should take 25 minutes for you to complete.
You won’t need to come prepared with intricate knowledge of software packaging, but familiarity with Linux paradigms, terminal operations, and ExpressJS is required.
Once you complete this tutorial, you’ll have a working rock for an ExpressJS
application. You’ll gain familiarity with Rockcraft and the
expressjs-framework
extension, and have the experience to create
rocks for ExpressJS applications.
Setup¶
We recommend starting from a clean Ubuntu installation. If we don’t have one available, we can create one using Multipass:
Is Multipass already installed and active? Check by running
snap services multipass
If we see the multipass
service but it isn’t “active”, then we’ll
need to run sudo snap start multipass
. On the other hand, if we get
an error saying snap "multipass" not found
, then we must install
Multipass:
sudo snap install multipass
See Multipass installation instructions, switch to Windows in the drop down.
See Multipass installation instructions, switch to macOS in the drop down.
Then we can create the VM with the following command:
multipass launch --disk 10G --name rock-dev 24.04
Finally, once the VM is up, open a shell into it:
multipass shell rock-dev
LXD will be required for building the rock. Make sure it is installed and initialised:
sudo snap install lxd
lxd init --auto
In order to create the rock, we’ll need to install Rockcraft:
sudo snap install rockcraft --classic --channel latest/edge
This tutorial requires the latest/edge
channel of Rockcraft as the
framework is currently experimental.
We’ll use Docker to run the rock. We can install it as a snap
:
sudo snap install docker
By default, Docker is only accessible with root privileges (sudo
). We want
to be able to use Docker commands as a regular user:
sudo addgroup --system docker
sudo adduser $USER docker
newgrp docker
Restart Docker:
sudo snap disable docker
sudo snap enable docker
Warning
There is a known connectivity issue with LXD and Docker. If we see a networking issue such as “A network related operation failed in a context of no network access”, make sure to apply one of the suggested fixes here.
Note that we’ll also need a text editor. We can either install one of our
choice or simply use one of the already existing editors in the Ubuntu
environment (like vi
).
This tutorial requires the latest/edge
channel of Rockcraft. Run
sudo snap refresh rockcraft --channel latest/edge
to get the latest
edge version.
In order to test the ExpressJS application locally, before packing it into a
rock, install npm
and initialize the starter app.
sudo apt-get update -y && sudo apt-get install npm -y
Create the ExpressJS application¶
Start by generating the ExpressJS starter template using the express-generator.
sudo npm install -g express-generator@4
express app
cd app && npm install
Let’s Run the Express application to verify that it works:
npm start
The application starts an HTTP server listening on port 3000
that we can test by using curl to send a request to the root
endpoint. We may need a new terminal for this – if using Multipass, run
multipass shell rock-dev
to get another terminal:
curl --fail localhost:3000
The ExpressJS application should respond with Welcome to Express web page.
Note
The response from the ExpressJS application includes HTML and CSS which
makes it difficult to read on a terminal. Visit http://localhost:3000
using a browser to see the fully rendered page.
The ExpressJS application looks good, so let’s stop it for now
with Ctrl + C, then move out of the application directory
cd ..
.
Pack the Express application into a rock¶
First, we’ll need a rockcraft.yaml
project file. Rockcraft will automate its
creation and tailor it for a ExpressJS application when we tell it to use the
expressjs-framework
profile:
rockcraft init --profile expressjs-framework
Open rockcraft.yaml
in a text editor and check that the name
key is set to expressjs-hello-world
. Ensure that platforms
includes
the architecture of the host. For example, if the host uses the ARM
architecture, include arm64
in platforms
.
As the expressjs-framework
extension is still experimental, export the
environment variable ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS
:
export ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true
Pack the rock:
rockcraft pack
Depending on the network, this step can take a couple of minutes to finish.
Once Rockcraft has finished packing the ExpressJS rock, we’ll find a new file in
the working directory (an OCI image) with the .rock
extension:
ls *.rock -l --block-size=MB
Run the ExpressJS rock with Docker¶
We already have the rock as an OCI image. Load the image into Docker:
sudo rockcraft.skopeo --insecure-policy \
copy oci-archive:expressjs-hello-world_0.1_amd64.rock \
docker-daemon:expressjs-hello-world:0.1
Check that the image was successfully loaded into Docker:
sudo docker images expressjs-hello-world:0.1
The output should list the ExpressJS image, along with its tag, ID and size:
REPOSITORY TAG IMAGE ID CREATED SIZE
expressjs-hello-world 0.1 30c7e5aed202 2 weeks ago 304MB
Now we’re finally ready to run the rock and test the containerised ExpressJS application:
sudo docker run --rm -d -p 3000:3000 \
--name expressjs-hello-world expressjs-hello-world:0.1
Use the same curl command as before to send a request to the ExpressJS application’s root endpoint which is running inside the container:
curl --fail localhost:3000
The ExpressJS application again responds with Welcome to Express page.
View the application logs¶
When deploying the ExpressJS rock, we can always get the application logs with Pebble:
sudo docker exec expressjs-hello-world pebble logs expressjs
As a result, Pebble will give us the logs for the
expressjs
service running inside the container.
We should expect to see something similar to this:
app@0.0.0 start
node ./bin/www
GET / 200 62.934 ms - 170
We can also choose to follow the logs by using the -f
option with the
pebble logs
command above. To stop following the logs, press Ctrl +
C.
Stop the application¶
Now we have a fully functional rock for a ExpressJS application! This concludes the first part of this tutorial, so we’ll stop the container and remove the respective image for now:
sudo docker stop expressjs-hello-world
sudo docker rmi expressjs-hello-world:0.1
Update the ExpressJS application¶
For our final task, let’s update our application. As an example,
let’s add a new /time
endpoint that returns the current time.
Start by creating the app/routes/time.js
file in a text editor and paste the
code from the snippet below:
var express = require('express');
var router = express.Router();
router.get('/', function (req, res, next) {
res.send(Date());
});
module.exports = router;
Place the code snippet below in app/app.js
under routes registration section
along with other app.use(...)
lines.
It will register the new /time
endpoint:
var timeRouter = require('./routes/time');
app.use('/time', timeRouter);
Since we are creating a new version of the application, set
version: '0.2'
in the project file.
Pack and run the rock using similar commands as before:
rockcraft pack
sudo rockcraft.skopeo --insecure-policy \
copy oci-archive:expressjs-hello-world_0.2_amd64.rock \
docker-daemon:expressjs-hello-world:0.2
sudo docker images expressjs-hello-world:0.2
sudo docker run --rm -d -p 3000:3000 \
--name expressjs-hello-world expressjs-hello-world:0.2
The resulting .rock
file will be named differently, as
its new version will be part of the filename.
Finally, use curl to send a request to the /time
endpoint:
curl --fail localhost:3000/time
The updated application should respond with the current date and time (e.g.
Fri Jan 10 2025 03:11:44 GMT+0000 (Coordinated Universal Time)
).
Tip
If you are getting a 404
for the /time
endpoint, check the
Troubleshooting steps below.
Final Cleanup¶
We can now stop the container and remove the corresponding image:
sudo docker stop expressjs-hello-world
sudo docker rmi expressjs-hello-world:0.2
Reset the environment¶
We’ve reached the end of this tutorial.
If we’d like to reset the working environment, we can simply run the following:
# delete all the files created during the tutorial
sudo npm uninstall -g express-generator@4
sudo apt-get remove npm -y
rm -rf app
rm expressjs-hello-world_0.1_amd64.rock \
expressjs-hello-world_0.2_amd64.rock \
rockcraft.yaml
If using Multipass...
If we created an instance using Multipass, we can also clean it up. Start by exiting it:
exit
And then we can proceed with its deletion:
multipass delete rock-dev
multipass purge
Next steps¶
Troubleshooting¶
Application updates not taking effect?
Upon changing the ExpressJS application and re-packing the rock, if
the changes are not taking effect, try running rockcraft clean
and pack
the rock again with rockcraft pack
.