diff --git a/deploy/nginx/Dockerfile b/deploy/nginx/Dockerfile new file mode 100644 index 00000000..76a56eb7 --- /dev/null +++ b/deploy/nginx/Dockerfile @@ -0,0 +1,36 @@ +# The site's nginx, with its configuration baked in. +# +# The configuration used to be bind-mounted out of the working tree, which made +# a working directory a deployment input: check out a branch and the routes +# inside the running container were that branch's, one reload away from live. +# That was not hypothetical -- a feature branch's /mcp route was found sitting +# in this container, inert only because nginx reads its configuration at start. +# +# Baked instead, which is what the other three services here already do +# (`render`, `content-node`, `mcp`). The running configuration is then a +# property of an image rather than of whatever happens to be checked out, and +# "which config is live" has an answer you can read off `docker image inspect` +# rather than infer. +# +# What is deliberately NOT baked: the certificates. Those are host secrets and +# stay as runtime mounts, so this image carries nothing that cannot be rebuilt +# from the repository. +FROM nginx:alpine + +# Which deployment this is. dev on the box that must not be indexed, production +# on the one that must -- the blanket bot rule makes that choice expensive to +# get wrong, which is why it is an explicit build argument rather than a +# default someone inherits. +ARG NGINX_ENV=dev + +COPY deploy/nginx/common /etc/nginx/common +COPY deploy/nginx/${NGINX_ENV}.conf /etc/nginx/conf.d/default.conf + +# Not `RUN nginx -t`: the configuration references certificates that are mounted +# at run time, so a build-time test would fail on every host for the wrong +# reason. Validate the built image before deploying it, which reads the same +# way and actually exercises the mounts: +# +# docker compose build nginx +# docker compose run --rm nginx nginx -t +# docker compose up -d nginx diff --git a/docker-compose.yml b/docker-compose.yml index 0a6544bd..88d8aeb9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -243,7 +243,13 @@ services: # To go back: `docker compose stop nginx && docker start reactome-nginx`. # Both bind the host's ports, so only one can run at a time. nginx: - image: nginx:alpine + build: + context: . + dockerfile: deploy/nginx/Dockerfile + args: + # Which deployment this host is. See deploy/nginx/README.md -- getting + # this wrong on the indexed host would deindex reactome.org. + NGINX_ENV: ${NGINX_ENV:-dev} restart: unless-stopped # Host networking, as the container it replaces used: the services it # proxies -- the site on 4200, content-node on 4400, render on 4310, the @@ -251,11 +257,8 @@ services: # deliberately so nothing else can. network_mode: host volumes: - # Which environment this host is. dev.conf here; production.conf on the - # host that must be indexed, where the blanket bot rule would be - # catastrophic rather than merely wrong. See deploy/nginx/README.md. - - ./deploy/nginx/${NGINX_ENV:-dev}.conf:/etc/nginx/conf.d/default.conf:ro - - ./deploy/nginx/common:/etc/nginx/common:ro + # Only the certificates are mounted. The configuration is baked into the + # image above, so a checkout cannot change what this serves. # The one part that cannot live in the repository. Paths are variables so # a laptop can point somewhere else, or at an empty directory when it is # serving plain HTTP and has no certificates at all.