Skip to main content

Run it in Docker

A container runs the site the same way on any machine that has Docker. This page builds an image from the download and runs it alone. It is for whoever manages the server. If you want a database or object storage as well, go on to the Compose pages afterwards.

What you need to know first​

  • No image is published and the download has no Dockerfile. You build your own, from the unpacked download. The Dockerfile below is short, because the download is already built and has its dependencies installed. It was built and run for this guide.
  • The container runs the launcher (node server/launcher.js), so one-click updates work. The updates are kept in the data volume.
  • It uses the node:22-alpine image, runs as the unprivileged user node, listens on port 3001, and keeps its data in /app/data.
  • Image and volume names use choir-manager, the product's older name.

1. Unpack and add two files​

mkdir choir-docker
tar -xzf choirmaster-cms-1.6.12.tar.gz -C choir-docker
cd choir-docker

Create Dockerfile in that folder:

# Choir Master CMS: one container built from the unpacked download.
FROM node:22-alpine
ENV NODE_ENV=production
WORKDIR /app
COPY . .
RUN mkdir -p /app/data && chown -R node:node /app
USER node
VOLUME ["/app/data"]
EXPOSE 3001
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s CMD wget -qO- http://localhost:3001/api/health || exit 1
CMD ["node", "server/launcher.js"]

And .dockerignore beside it:

.env
.env.*
data
secrets
deploy
Dockerfile
.dockerignore
docker-compose*.yml
danger

Do not skip .dockerignore. The Dockerfile copies the whole folder into the image, and without it your .env file, with its passwords and licence key, would be baked into the image. The same goes for a secrets folder, if you use Docker secrets later (see Docker: HTTPS, ports and volumes).

LineWhat it does
FROM node:22-alpineA small image with Node 22.
ENV NODE_ENV=productionProduction mode: Secure cookies and SITE_URL required. It is also the server's default.
COPY . .Puts the unpacked download in /app. There is nothing to build or install.
chown -R node:node /appLets the unprivileged user write, so it can make the database and keep downloaded updates.
VOLUME ["/app/data"]Marks where the data lives, so Docker keeps it apart from the container. Always mount a named volume there (below).
HEALTHCHECKDocker asks /api/health every 30 seconds and shows the container as healthy.
CMDStarts the launcher.

2. Write the settings file​

Create .env in the same folder:

SITE_URL=https://choir.example.org
ADMIN_USERNAME=admin
ADMIN_PASSWORD=change-me-to-something-long-and-random
LICENSE_KEY=cmk_your_key_here
EMAIL_PROVIDER=log
EMAIL_FROM=Harmony Community Choir <noreply@example.org>
warning

For docker run --env-file, write values without quotes. Unlike most settings files, Docker keeps the quotes as part of the value, so EMAIL_FROM="Harmony ..." would send mail from an address that starts with a quote mark. Compose removes the quotes, so there either way works.

This is the small version. What each setting does, and the full list, are in The settings every site needs. Replace log with a real email service before the choir uses the site: Email: what the site sends and how to choose a provider.

The container's defaults are SQLite at ./data/choir.sqlite and local files at ./data/storage, both inside the volume. Do not set SQLITE_PATH or STORAGE_LOCAL_DIR unless you want them elsewhere.

3. Build and run​

docker build -t choir-manager .
docker run -d --name choir-manager \
--restart unless-stopped \
-p 8080:3001 \
--env-file .env \
-v choir-data:/app/data \
choir-manager
PartMeaning
-p 8080:3001Publish the container's port 3001 on the host's port 8080. Change 8080 to change the host port.
--env-file .envPass the settings in.
-v choir-data:/app/dataA named volume, choir-data, that holds the database, uploads, downloaded releases and pre-update copies. It survives removing and recreating the container.
--restart unless-stoppedStart it again after a crash or a reboot of the host.

4. Check it​

docker ps
curl http://localhost:8080/api/health
docker logs choir-manager

After about 15 seconds docker ps shows (healthy). The health answer is {"status":"ok","platform":"node","version":"1.6.12"}. The log shows [launcher] starting 1.6.12, any [config] warnings, [db] schema is up to date (sqlite) and [server] version 1.6.12 listening on http://localhost:3001 (production).

The site is on port 8080 of the host over plain http. A real site needs HTTPS in front of it: see Put it behind HTTPS (proxy to localhost:8080) or Docker: HTTPS, ports and volumes.

Run the helper commands​

The commands from What is in the download run inside the container:

docker exec choir-manager npm run config:check
docker exec choir-manager npm run admin:add -- someone@example.org "Their Name"

Stop, restart, remove​

docker stop choir-manager
docker start choir-manager
docker rm -f choir-manager # removes the container; the choir-data volume stays

Changing the settings file means recreating the container (docker rm -f and docker run again), because the settings are read when it starts.

Updates​

With a licence key, the admin panel can install a new version itself: the release is kept in the volume and used until the image is as new. See Update a Docker site.

Next​