Playing with Docker on Unraid from Windows 10
Tags: papertrail, docker, unraid, windows
Published: 2021-03-16 20:22:53

I've recently setup an Unraid server in my house and wanted to use the opportunity to learn a bit about Docker by developing a simple web app and deploy it to the server.

I'm writing this series to leave a paper trail for other people to follow since I ran into a number of issues.

**I am by no means a docker expert**. This is all from the point of view of a docker novice.

First a few basic details about my setup:

 * It's March 2021
 * Development PC is a Windows 10 version: 10.0.19042.867
 * Unraid is version: 6.9.1
 * Docker Engine is version: 20.10.5
 * I've installed WSL2 

Here are some links to get the basics up and running:

 * [Unraid installation](https://wiki.unraid.net/Articles/Getting_Started)
 * [WSL2 for Windows 10](https://docs.microsoft.com/en-us/windows/wsl/install-win10)
 * [Docker for Windows using WSL2](https://docs.docker.com/docker-for-windows/wsl/)

The web app I'm building has a frontend (Angular/Ionic) and a backend (NodeJS). I'm not really going to go into those things as this is mostly about just getting docker working on Unraid.

---

# Creating the Docker images and containers for my App
Initially I was going to use PHP for the server - because it's what I already had - but I found this excellent:

[NGINX with Docker and Node.js — a Beginner’s guide](https://ashwin9798.medium.com/nginx-with-docker-and-node-js-a-beginners-guide-434fe1216b6b)

The end product of this guide is an NGINX container that serves as a reverse proxy for a Node.js server. 

I didn't know what a reverse proxy was, but essentially it lets you access another service through the endpoint of NGINX. You can configure NGINX to pass your requests to another server and ferry the response back. This can be helpful if you want to hide the actual location of the other server. NGINX can also take care of all the HTTPS security stuff for your reversed proxied server.

This guide doesn't add a frontend so I thought I'd try to do that myself and followed the documentation from NGINX: [NGINX Docs | Serving Static Content](https://docs.nginx.com/nginx/admin-guide/web-server/serving-static-content/#root0)

## Setting up the NGINX docker container
It only requires 2 files to setup NGINX in a container

 * `Dockerfile` the instructions to create the image.
 * `default.conf` the configuration of the server.

That's about as easy as anyone could hope for. 

The `Dockerfile` literally just splats the configuration onto the server and you're done.

```
FROM nginx
COPY default.conf /etc/nginx/conf.d/default.conf
```

Now my `default.conf` looks like this:

```
server {
  root /data;

  location /media {
    rewrite ^/media(.*)$ $1 break;
    root /media;
  }
  
  location /nodeserver {
    rewrite ^/nodeserver(.*)$ /$1 break;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    proxy_pass http://nodeserver:5000;
  }
}
```

This is somewhat similar to Ashwin's guide, but I'll try to explain what I'm doing here.

First up `root /data;` is the root directory used to try to find the static content. I'm expecting the container to be able to find some HTML (particularly `index.html`) in the `/data` folder. I'll accomplish this later by **mounting** a folder on my system to the  `/data` folder in the container.

The `location /media` block I use to serve some media files from another folder separate from the `/data` folder. Whenever someone requests a url that starts with `/media/`, then we serve from `root /media;` instead of the default root.

I did this without the `rewrite` line first, but then my requests would all be prefixed with `/media` so when I request `http://localhost/media/myfile.mp4` NGINX would try to serve the file `/media/media/myfile.mp4`. To remove the first `/media` I used this rewrite rule. A solution I found here: [serverfault | how to remove location block from $uri in nginx configuration?](https://serverfault.com/questions/455799/how-to-remove-location-block-from-uri-in-nginx-configuration)

Finally, there is the `/nodeserver` location, this is my access to the backend. Here I setup the reverse proxy as described in Ashwin's guide. I use the same rewrite rule here to remove the prefix from the requests forwarded to the backend. It's really crucial to note the / prefix in `/$1`, this won't work with just `$1`. I found the solution to that [here](https://serverfault.com/questions/379675/nginx-reverse-proxy-url-rewrite)

## Node.JS container setup
I used the same approach as in Ashwin's guide. 

Here is the `Dockerfile` I used to create my node server:
```
FROM node:alpine
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 5000
CMD ["node", "index.js"]
```

## docker-compose for local deployment
I found Docker quite cumbersome before I made it to the `docker-compose` stage. It does make it a lot easier.

For a local deployment of this app, I need to spin up my two containers together and mount in a `/data` and a `/media` folder.

`docker-compose.yml` for that looks something like this:

```
version: "3.8"
services:
  nodeserver:
    build:
      # home-video-server has the nodeserver node.js DockerFile
      context: ./home-video-server
  nginx:
    restart: always
    build:
      # home-server-nginx has the NGINX DockerFile
      context: ./home-server-nginx
    ports:
      - "81:80"
    volumes:
      # Map the subfolder home-video/www to the /data folder
      - ./home-video/www:/data
      # and put my videos in the media folder
      - c:\Users\jacob\videos:/media
```

You can use `docker-compose up --build` to update the images and restart the containers.

In the default.conf for the NGINX server, you may have noticed that it refers to nodeserver by `http://nodeserver:5000/` this kind of reference is only possible because we used docker compose with both containers. It's facilitated by a docker network that's created automatically. See [Docker Compose Documentation on Networking](https://docs.docker.com/compose/networking/).

---

Next time I'll explain how I managed to deploy the containers to my Unraid server using SSH.