From the perspective of Docker, the only two parts of the stack that need to be deployed are PostgreSQL and Node.js. This stack will also include Adminer which gives you a database UI. Node is the server and server-side framework, Express.js adds syntactic sugar to Node and allows you to do common tasks far simpler and React.js is a front-end framework that will control the presentation of data.
A problem that I ran across when using Docker Compose by itself was that Node didn’t have any running services by default. If you start a Node container on its own, it will turn off immediately after starting it. This is because there are no server configurations or start scripts that are being ran when you start a node container, just a Linux environment with node.js installed. So we will need a Dockerfile to create a custom express.js image. The express server will be set to start whenever the container is started.
Eventually I may create an image on Dockerhub called ryanbj/expressjs
. It will look similar to the following:
FROM node:latest
# Enviornment Variables
ARG PORT=3000
ENV NODE_PORT=$PORT
# Expose ports
EXPOSE $NODE_PORT/tcp
# Set Working directory to node root
WORKDIR /home/node
# Install Express and Nodemon
RUN npm update && \
npm -y init && \
npm install express && \
npm install -D nodemon
# Set up environment
RUN sed -i '/"version".*/a \ \ "type": "module",' package.json && \
sed -i 's/"test".*/"start": "node index",/' package.json && \
sed -i '/"start".*/a \ \ \ \ "dev": "nodemon index"' package.json
# Create a server start script
COPY index.js .
# Set Working directory to node root parent
WORKDIR /home
# Load run-time configuration changes
COPY entrypoint.sh .
RUN chmod +x entrypoint.sh
ENTRYPOINT [ "/home/entrypoint.sh" ]
# Command to start server
CMD ["/bin/bash", "-c", "npm --prefix /home/node run start"]
This is the first-gen instruction set to build a docker image for express.js. It requires a start script for the server to run (index.js) and a script file for run-time configurations (entrypoint.sh).
import express from 'express'
const app = express()
const port = process.env.PORT || 3000
app.get('/', (req, res) => {
res.send('<h1>Hello from Express</h1>')
})
app.listen(port, () => {
console.log(`App listening on port ${port}`)
})
This is the javascript file that will get executed by node on container start. It starts a simple Express server.
#!/bin/bash
sed -i 's/3000/'$NODE_PORT'/g' /home/node/index.js
exec "$@"
This will run on docker run
or docker create
to customize the configurations of the image. In this case, it will change the port node is listening on from 3000 to whatever is specified in the environment variable.
Because the Dockerfile requires two other files to be present when you build the image, you will need to be in the directory where all the files are located before running the build command.
If the dockerfile is named Dockerfile
you can build the image with the following command:
docker image build .
If the dockerfile is in a different location, you can add the -f
flag.
docker image build -f {path}/{dockerfile} .
In some cases, you may need to compress all of the files in your dockerfile context into a tar archive.
On Mac/Linux, add all of the files (Dockerfile, index.js, and entrypoint.sh) into a folder and run:
tar -czf {directory name}.tgz {directory name}
If you need to extract it for whatever reason, you can use tar -xvf {directory name}.tgz
On Windows, you will need a archive utility like 7zip. Select all of the files, right-click, select 7zip, and then “Add to Archive”. When customizing the archive, select “tar” as compression type and then click “Create Archive”.
To build the image using a tar archive:
docker image build {directory name}.tgz
version: '3.1'
services:
db:
image: postgres
restart: always
ports:
- "3307":"3307"
volumes:
- v_welp_db:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: {password}
POSTGRES_USER: {user}
POSTGRES_DB: {database}
adminer:
image: adminer
restart: always
ports:
- "8180":"8080"
node:
image: node
restart: always
ports:
- "8084":"3000"
volumes:
- {host path}:{container path}
volumes:
v_welp_db: {}
To deploy the stack you can navigate to the directory of the compose file and run:
docker compose up