I have an Nginx container set up which serves assets for a static website. The idea is for the webserver to always stay up, and overwrite the assets whenever they are recompiled. Currently the docker setup looks like this:
docker-compose.yml:
version: '3'
services:
web:
build: ./app
volumes:
- site-assets:/app/dist:ro
nginx:
build: ./nginx
ports:
- 80:80
- 443:443
volumes:
- site-assets:/app:ro
- https-certs:/etc/nginx/certs:ro
depends_on:
- web
volumes:
site-assets:
https-certs:
Web (asset-builder) Dockerfile:
FROM node:latest
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY ./ .
RUN npm run generate
Nginx Dockerfile:
FROM nginx:latest
RUN mkdir /app
COPY nginx.conf /etc/nginx/nginx.conf
The certbot container is managed separately and is not relevant to the problem I'm having, but the Nginx container does need to be able to mount the https-certs volume.
This setup seemed good, until I realized the site-assets volume would not be updated after first creation. The volume would need to be destroyed and re-created on each app deployment for this to work, requiring the Nginx container to be stopped to unmount the volume. So much for that approach.
Is there a way to manage application data in this setup without bringing the Nginx container down? Preferably, I would want to do this declaratively with a docker-compose file, avoid multiple application instances as this doesn't need to scale, and avoid using docker inspect
to find the volume on the filesystem and modify it directly.
I hope there is a sane answer to this other than "It's a static site, why aren't you using Netlify or GitHub Pages?" :)
Here is an example that would move your npm run generate
from image build time to container run time. It is a minimal example to illustrate how moving the process to the run time makes the volume available to both the running container at startup and future ones at run time.
With the following docker-compose.yml
:
version: '3'
services:
web:
image: ubuntu
volumes:
- site-assets:/app/dist
command: bash -c "echo initial > /app/dist/file"
restart: "no"
nginx:
image: ubuntu
volumes:
- site-assets:/app:ro
command: bash -c "while true; do cat /app/file; sleep 5; done"
volumes:
site-assets:
We can launch it with docker-compose up
in a terminal. Our nginx
server will initially miss the data but the initial web
service will launch and generate our asset (with contents initial
):
❯ docker-compose up
Creating network "multivol_default" with the default driver
Creating volume "multivol_site-assets" with default driver
Creating multivol_web_1 ... done
Creating multivol_nginx_1 ... done
Attaching to multivol_nginx_1, multivol_web_1
nginx_1 | cat: /app/file: No such file or directory
multivol_web_1 exited with code 0
nginx_1 | initial
nginx_1 | initial
nginx_1 | initial
nginx_1 | initial
In another terminal we can update our asset (your npm run generate
command):
❯ docker-compose run web bash -c "echo updated > /app/dist/file"
And now we can see our nginx
service serving the updated content:
❯ docker-compose up
Creating network "multivol_default" with the default driver
Creating volume "multivol_site-assets" with default driver
Creating multivol_web_1 ... done
Creating multivol_nginx_1 ... done
Attaching to multivol_nginx_1, multivol_web_1
nginx_1 | cat: /app/file: No such file or directory
multivol_web_1 exited with code 0
nginx_1 | initial
nginx_1 | initial
nginx_1 | initial
nginx_1 | initial
nginx_1 | updated
nginx_1 | updated
nginx_1 | updated
nginx_1 | updated
^CGracefully stopping... (press Ctrl+C again to force)
Stopping multivol_nginx_1 ... done
Hope this was helpful to illustrate a way to take advantage of volume mounting at container run time.