This is the fourth part in a series focused on teaching the fundamentals of building and developing applications using Acorn. In the previous post of this series we enhanced the Acornfile of the VotingApp defining volumes for both db and redis containers. In this post we will use Acorn’s development mode to accelerate and ease the development workflow.
About development mode
In development mode, Acorn allows you to make changes to the source code and see it updated inside the app containers in real time. In this mode Acorn will watch the local directory for changes and synchronize them to the running Acorn app. To activate the development mode we need to use the -i flag when running the app.
In this post we will focus on the result microservice which is developed with Node.js. If you have a look into the folder containing the application code, you will see 2 build targets are defined in the Dockerfile: the first one is named dev, the second one production:
FROM node:18.12.1-slim as base
WORKDIR /app
COPY . .
EXPOSE 5000
FROM base as dev
ENV NODE_ENV=development
RUN npm ci
CMD ["npm", "run", "dev"]
FROM base as production
ENV NODE_ENV=production
RUN npm ci --production
CMD ["npm", "start"]
– when the image is built for the dev target, the command npm run dev
runs nodemon under the hood. nodemon is a process which watches the code changes and which is able to relaunch the application
– when the image is built for the production target, the command npm start
runs the standard node binary. No hot reload is possible in that case
Before running the Acorn application in development mode we need to modify the Acornfile is bit to make sure that if the dev mode is detected:
- the code folder of the result microservice is mounted into the /app folder within the container
- the build is done against the dev target, this will ensure nodemon is the main process running in the result container thus making hot reload possible
Modify the definition of the result container so it looks as follows:
result: {
build: {
target: std.ifelse(args.dev, "dev", "production")
context: "./result"
}
if args.dev {
dirs: {
"/app": "./result"
}
}
ports: "5000/http"
env: {
"POSTGRES_USER": "secret://db-creds/username"
"POSTGRES_PASSWORD": "secret://db-creds/password"
}
}
Note: Acorn provides many useful functions such as the std.ifelse, an helper to perform an if…else…end statement on a single line. All the function available are listed in the function library documentation.
You can now update the application running it in development mode:
acorn run -n vote -i --update .
note: in development mode you’ll notice that the logs of each containers are streamed to the console
Once the application is up and running you can modify the server.js file located in the result folder and see that changes automatically been taken into account making the container’s process to restart.
Below is an example of logs you can get for the result service following a change in server.js:

We illustrated above the development mode for the result microservice but the same principles would apply for the other microservices as well. We can change the Acornfile modifying the definition of the voteui, vote and resultui containers to ensure the development mode is working fine for those ones as well.
voteui: {
if args.dev {
dirs: {
"/usr/share/nginx/html": "./vote-ui"
}
}
build: {
context: "./vote-ui"
}
ports: publish : "80/http"
}
vote: {
build: {
target: std.ifelse(args.dev, "dev", "production")
context: "./vote"
}
if args.dev {
dirs: {
"/app": "./vote"
}
}
ports: "5000/http"
}
resultui: {
build: {
target: std.ifelse(args.dev, "dev", "production")
context: "./result-ui"
}
if args.dev {
dirs: {
"/app": "./result-ui"
}
}
ports: publish : "80/http"
}
Once you have modified the Acornfile you can update the application one more time:
acorn run -n vote -i --update .
We can go one step further and make sure volumes are not used when the app is run in development mode.
In order to do that we need to:
- add a condition in the volumes key so that no volumes are created in dev mode:
volumes: {
if !args.dev {
"db": {
size: "100M"
}
"redis": {
size: "100M"
}
}
}
- add a condition in redis container so content of /data is not persisted in a volume while in dev mode
redis: {
labels: {
component: "redis"
}
image: "redis:7.0.5-alpine3.16"
ports: "6379/tcp"
dirs: {
if !args.dev {
"/data": "volume://redis"
}
}
}
- add a condition in db container so content of /var/lib/postgresql/data is not persisted in a volume while in dev mode
db: {
labels: {
component: "db"
}
image: "postgres:15.0-alpine3.16"
ports: "5432/tcp"
env: {
"POSTGRES_USER": "secret://db-creds/username"
"POSTGRES_PASSWORD": "secret://db-creds/password"
}
dirs: {
if !args.dev {
"/var/lib/postgresql/data": "volume://db"
}
}
}
Wrapping up
In this example we explained how to use Acorn development mode to speed up the whole development cycle of the application as changes done locally are immediately taken into account by the running containers.
In the next post of the series we’ll discuss about args and profile which are often used to provide a dynamic configuration to an application. If you’d like to learn more about working with Acorn, you can register for an upcoming live online training.
Luc Juggery is a software engineer with 18+ years of experience and co-founder of 2 startups located in Sophia-Antipolis, southern France. You can chat with him on Twitter, read more of his work on Medium, find his tutorials on YouTube, or take one of his Docker or Kubernetes training courses on Udemy.