<- all articles

Setting up Hot Reloading behind a Reverse Proxy

If you’re a fan of Docker, you’ve probably heard of or worked with Docker Compose. In a nutshell, Docker Compose allows you to organize multiple containers to work together in a precisely orchestrated manner. Moreover, it will enable you to quickly spin up a network of interconnected containers and services so that you need to make zero configs on your machine.

At Wheelhouse, we’re on the job as consultants for several projects and have seen it used in various ways. For some reason, the problem of having Hot Reloading behind a reverse proxy feels cumbersome but is not too hard to tackle. For that reason, we’re writing a short blog on one way to configure something like this practically.

Why Hot Reloading is, in fact, hot 🔥

Hot Reload is when your browser will refresh automatically to reflect the changes you made in the editor. It’s a developer concern and is not something that runs in production. You change a file in the source files of the app you’re working on, and the browser will immediately show you the latest changes. This is a big deal for front-end developers. This is so standard that it’s outrageous and even cumbersome to work without it.

Imagine a project you check out and find to have been completely orchestrated with Docker Compose. The only thing is that you find out hot-reloading isn’t working because of an issue with the reverse proxy within the stack.

Why is there a reverse proxy? 🐙

As said before, sometimes we’re on the job with our clients, working on the infrastructure already in place. One of my jobs was to upgrade some old technology to new standards. Instead of dealing with the entire refactor as one big chunk, I communicated with my client that we would refactor the system per URL for each module, allowing us to use a reverse proxy to redirect traffic to new modules within Docker Compose.

We were able to mimic the production environment this way. We could also steadily phase out the old PHP part without having to do it in one go. One caveat, of course, is that the specific developer concern of Hot Reloading does not work out of the box!

The solution 🧪

The solution - or at least one possible solution to the problem - is to configure the reverse proxy and the modules specifically to allow web sockets! These are the backbone of hot-reloading, basically, and your browser needs to be able to establish a connection (via the reverse proxy) to the dev-server running on the module within Docker Compose.

How to set it up

Considering the following docker-compose.yml:

version: "3.1"
services:
 reverse-proxy:
   ...
 old-php:
   ...
 registration-module:
   ...
 some-other-new-module:
   ...

The reverse-proxy here is running nginx. The old-php container runs the entire system (in the legacy PHP). As we mentioned before, the other modules would override the php at specific URLs. The technology in these containers does not matter, but as an example, we’re using Vue with the dev-server that comes with the Webpack version of the developer boilerplate (with vue-cli).

We expose the nginx config in our project using volumes in Docker Compose. The config file looks like this at the moment:

server {  listen 8081;  # Pass any traffic over to PHP legacy  location / {    proxy_pass http://old-php;  }  # Pass traffic to registration-module  location /register/ {    proxy_pass http://registration-module:8000;  }}

This passes all traffic by default to the legacy, and any specific URLs are overridden and redirected to their modules. That’s basically how the reverse proxy should work. Let’s add a tiny but essential bit of code:

 # Pass traffic to registration-module
  location /register/ {
    proxy_pass http://registration-module:8000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }

This lets through any Web Socket connections, which we need for the hot-reload service. Now, this in itself will not work, because each module (in Vue) thinks they are running at the root / which they aren’t; they run at a directory. In this case /register/ and that must also be configured.

For Vue, we can find this in the vue.config.js file at the root of the Registration Module:

module.exports = {  devServer: {    port: 8000,    disableHostCheck: true,    public: `http://localhost:3000/register/`,    sockPath: `/register/sockjs-node`,  },};

We make sure to set the port to 8000. You may choose which port you use, but make sure it is consistent with the one used in the reverse proxy! The disableHostCheck has been deprecated at the time of this writing in favor of allowedHosts, which should be ‘All’. The public path must be changed to include the subdirectory for the module; otherwise, the URL will be re-written, and the app won’t work! The sockPath is perhaps the most notable and essential option. This tells the Webpack Dev Server which URL to put the WebSocket handle. The front-end in development mode will look for this node relative to the URL in the browser, and we just changed the public path as well. Not setting this with the correct subdirectory will inevitably fail.

That’s it! The combination of these settings will forward and allow Hot Reloading so you can have a better developer experience. Never accept it when your Hot Reloading is not working; you must refresh the page to see changes. Your time is worth a lot more. Enjoy developing! ✨

Written by

Want to know more?

Related articles