<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jacob Korsgaard</title>
    <description>The latest articles on DEV Community by Jacob Korsgaard (@felizk).</description>
    <link>https://dev.to/felizk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F597270%2F00a3c996-aac8-4ce0-81e3-a34906792c48.png</url>
      <title>DEV Community: Jacob Korsgaard</title>
      <link>https://dev.to/felizk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/felizk"/>
    <language>en</language>
    <item>
      <title>[Solved] iPhone "The device is unreachable"</title>
      <dc:creator>Jacob Korsgaard</dc:creator>
      <pubDate>Sat, 06 May 2023 14:09:05 +0000</pubDate>
      <link>https://dev.to/felizk/iphone-copy-photos-to-pc-n1b</link>
      <guid>https://dev.to/felizk/iphone-copy-photos-to-pc-n1b</guid>
      <description>&lt;p&gt;If you've ever tried copying photos from your iPhone using a PC, you may have encountered an issue that goes something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The device is unreachable"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You may also have had issues with the iPhone disconnecting from the PC while you're trying to copy photos. &lt;/p&gt;

&lt;p&gt;I have finally found out why this is happening. It turns out it's because the iPhone runs out of space!&lt;/p&gt;

&lt;p&gt;For every copy operation you do from the phone to the PC, an additional copy is created on the phone in a buffer!&lt;/p&gt;

&lt;p&gt;To ensure success, clear up space on your iPhone if you can, then reboot the iPhone to clear out any buffers. Then copy off the biggest files off the iPhone 1 by 1, delete them from the phone and reboot. Slowly build up space on the phone so further operations succeed.&lt;/p&gt;

&lt;p&gt;Whenever you get the error, reboot the iPhone to clear up the buffer used and try again.&lt;/p&gt;

</description>
      <category>ios</category>
    </item>
    <item>
      <title>Get Azure Pipelines UniversalPackages version</title>
      <dc:creator>Jacob Korsgaard</dc:creator>
      <pubDate>Fri, 16 Apr 2021 07:29:39 +0000</pubDate>
      <link>https://dev.to/felizk/get-azure-pipelines-universalpackages-version-fam</link>
      <guid>https://dev.to/felizk/get-azure-pipelines-universalpackages-version-fam</guid>
      <description>&lt;p&gt;I was building an azure pipelines job and during it I uploaded a Universal Package using the &lt;code&gt;UniversalPackages&lt;/code&gt; task. &lt;/p&gt;

&lt;p&gt;I then had to contact a REST API with the package and version of the newly published Universal Package. To do that I need to get the version and package name out of the task.&lt;/p&gt;

&lt;p&gt;That looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    - task: UniversalPackages@0
      displayName: Universal Publish
      inputs:
        command: publish
        publishDirectory: '$(System.DefaultWorkingDirectory)/dist'
        vstsFeedPublish: 'MyProject/MyFeed'
        vstsFeedPackagePublish: 'package.name'
        packagePublishDescription: 'My super package'
        versionOption: patch #increment the patch part of the semantic versioning
        publishedPackageVar: someVariableName
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this task runs &lt;code&gt;$(someVariableName)&lt;/code&gt; will hold a string like so "package.name 1.0.1", but that variable will not be available outside this current job. &lt;/p&gt;

&lt;p&gt;To make it available for other jobs you need to move it to an output variable. I did that like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    - powershell: echo "##vso[task.setvariable variable=PackageNameAndVersion;isOutput=true]$(someVariableName)"
      name: PackagePublish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the value is available in other jobs through &lt;code&gt;PackagePublish.PackageNameAndVersion&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can consume it in subsequent jobs by making sure those jobs depend on this job and then grabbing the variable from the output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- job: 'RestAPICallingJob'
  dependsOn: 'BuildProductionStuff'
  pool: server
  variables:
    PackageNameAndVersion: $[ dependencies.BuildProductionStuff.outputs['PackagePublish.PackageNameAndVersion'] ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I can add a step to call a REST API using the package name and version of my universal package.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; I ended up not using Universal Packages, because you can only download Universal Packages from Azure using the AzureCLI. Instead, I created a NuGet package that contained just my loose files using a .nuspec file.&lt;/p&gt;

</description>
      <category>papertrail</category>
      <category>azure</category>
      <category>pipelines</category>
    </item>
    <item>
      <title>RClone in a Docker to backup OneDrive locally</title>
      <dc:creator>Jacob Korsgaard</dc:creator>
      <pubDate>Sat, 20 Mar 2021 15:54:27 +0000</pubDate>
      <link>https://dev.to/felizk/rclone-in-a-docker-to-backup-onedrive-locally-4ble</link>
      <guid>https://dev.to/felizk/rclone-in-a-docker-to-backup-onedrive-locally-4ble</guid>
      <description>&lt;p&gt;Today I'm trying to setup my Unraid server to take a daily backup of the contents of my OneDrive folder. That's in case of catastrophic failure on Microsoft's side, or more likely, I mess up and nuke my OneDrive folder for whatever reason. It's also just nice to have the data available locally.&lt;/p&gt;

&lt;p&gt;To accomplish this task I'm using &lt;a href="https://rclone.org/"&gt;&lt;strong&gt;RClone&lt;/strong&gt;&lt;/a&gt; and I'm using it from a Docker container, primarily to continue on my learning journey with Docker. It's also nice that I could test things out on my Windows PC and easily move the setup to my Unraid server.&lt;/p&gt;

&lt;p&gt;The RClone image I used is &lt;code&gt;rclone/rclone:latest&lt;/code&gt;. That said, in order to actually get started with RClone you need to configure it first, that is give it access to whatever cloud service you want to access.&lt;/p&gt;

&lt;p&gt;To do this I downloaded the Windows binary instead, because RClone has a simple interactive flow you can use to create a configuration file. See instructions &lt;a href="https://rclone.org/install/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After running the configuration I had an rclone.conf file ready to go. I deployed this to the Unraid server and set about defining the command-line to use to backup my Unraid settings.&lt;/p&gt;

&lt;p&gt;I went with a &lt;strong&gt;sync&lt;/strong&gt; command parameterized with &lt;code&gt;--backup-dir&lt;/code&gt;. Then I set the command to run using the &lt;em&gt;"Community Applications User Scripts"&lt;/em&gt; plugin on Unraid (which is basically just an easy way to setup cron jobs from the Unraid WebUI)&lt;/p&gt;

&lt;p&gt;The final bash script looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash
NOW=$(date +"%Y-%m-%d")
docker run --rm \
  --volume /mnt/user/appdata/rclone:/config/rclone \
  --volume /mnt/user/onedrive:/data \
  rclone/rclone sync onedrive: /data/root \
  --backup-dir /data/backup/$NOW
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First we create a date string for the backup folder. The backup folder is used whenever a file is deleted by the sync operation. &lt;em&gt;Note: It's likely I'll want to run a deduplication script after syncing so the backup folder doesn't end up full of files that I already backed up.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then we mount in the config folder for rclone as well as the target folder to copy the files to.&lt;/p&gt;

&lt;p&gt;Finally, we run the RClone command with the root OneDrive folder as source &lt;code&gt;onedrive:&lt;/code&gt; and the mounted folder as the target &lt;code&gt;/data/root&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;onedrive:&lt;/code&gt; label is the one I created for my connection to OneDrive during configuration of RClone.&lt;/p&gt;

&lt;p&gt;The one remaining niggle with the result is that the files created are all read/write only by root and read-only by everyone else. For my purpose, that's fine.&lt;/p&gt;

</description>
      <category>unraid</category>
      <category>rclone</category>
      <category>onedrive</category>
      <category>docker</category>
    </item>
    <item>
      <title>Node.js Docker Service Tips</title>
      <dc:creator>Jacob Korsgaard</dc:creator>
      <pubDate>Tue, 16 Mar 2021 21:46:23 +0000</pubDate>
      <link>https://dev.to/felizk/node-js-docker-service-tips-4md3</link>
      <guid>https://dev.to/felizk/node-js-docker-service-tips-4md3</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/felizk/remote-deploy-with-docker-compose-to-unraid-1dai"&gt;Last time&lt;/a&gt; I deployed some docker containers remotely to Unraid. Now let me share a tip or two for the node.js part of those containers.&lt;/p&gt;




&lt;h1&gt;
  
  
  Graceful exit
&lt;/h1&gt;

&lt;p&gt;Node.js applications do not automatically know how to gracefully shut down when a container is stopped. To do this you'll have to handle signals.&lt;/p&gt;

&lt;p&gt;I followed this nice guide: &lt;a href="https://medium.com/@becintec/building-graceful-node-applications-in-docker-4d2cd4d5d392"&gt;Building Graceful Node Applications in Docker&lt;/a&gt; and they got the signal handling from here &lt;a href="https://medium.com/@gchudnov/trapping-signals-in-docker-containers-7a57fdda7d86"&gt;Trapping signals in Docker containers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The crux of the code is to add a listener to &lt;code&gt;process&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function shutdown() {
  // close your server here
  process.exit();
}

process.on('SIGTERM', shutdown); 
process.on('SIGINT', shutdown); 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://nodejs.org/api/process.html#process_signal_events"&gt;Node.JS Signal Events Documentation&lt;/a&gt; doesn't call &lt;code&gt;process.exit()&lt;/code&gt; but it turns out Docker does funny things due to the process having ID 1, this is the most up to date post I've found about that issue: &lt;a href="https://www.docker.com/blog/keep-nodejs-rockin-in-docker/"&gt;Top 4 Tactics To Keep Node.js Rockin’ in Docker&lt;/a&gt; see "Start Node Directly in Dockerfiles".&lt;/p&gt;




&lt;h1&gt;
  
  
  Container content not updating
&lt;/h1&gt;

&lt;p&gt;Another issue I ran into with my node.js container was that when I made changes to my &lt;code&gt;index.js&lt;/code&gt; and ran the command to deploy my containers to Unraid, the Unraid nodeserver container would not have my changes.&lt;/p&gt;

&lt;p&gt;I searched for a solution and &lt;code&gt;-no-cache&lt;/code&gt; and &lt;code&gt;--force-recreate&lt;/code&gt; were both suggested, but these methods did not fix the problem.&lt;/p&gt;

&lt;p&gt;The only thing I found that actually fixed the issue was to remove the container and images from Unraid before building new ones and deploying those.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose --context unraid -f docker-compose.yml -f docker-compose.prod.yml down --rmi local
docker-compose --context unraid -f docker-compose.yml -f docker-compose.prod.yml up --build -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, arguably an even better way would be to not put the index.js into the container, but instead mount a path to run index.js from. Then just restart the nodeserver container when I made changes. Which is probably what I would do in the future for iteration. &lt;/p&gt;

</description>
      <category>unraid</category>
      <category>windows</category>
      <category>docker</category>
      <category>papertrail</category>
    </item>
    <item>
      <title>Remote Deploy with Docker-Compose to Unraid</title>
      <dc:creator>Jacob Korsgaard</dc:creator>
      <pubDate>Tue, 16 Mar 2021 21:17:23 +0000</pubDate>
      <link>https://dev.to/felizk/remote-deploy-with-docker-compose-to-unraid-1dai</link>
      <guid>https://dev.to/felizk/remote-deploy-with-docker-compose-to-unraid-1dai</guid>
      <description>&lt;p&gt;In my &lt;a href="https://dev.to/felizk/playing-with-docker-on-unraid-from-windows-10-4060"&gt;previous post&lt;/a&gt; I built a couple of containers with docker-compose. Now I'd like to share how I deploy those containers to my Unraid server using SSH.&lt;/p&gt;

&lt;p&gt;I've had two goals, from my Windows machine I'd like to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploy my containers to Unraid&lt;/li&gt;
&lt;li&gt;Iterate on the containers locally&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Multiple configurations with docker-compose
&lt;/h1&gt;

&lt;p&gt;The first thing I tackled was figuring out how to customize my docker compose files based on the target I'm building for.&lt;/p&gt;

&lt;p&gt;It turns out the tool for that is &lt;a href="https://docs.docker.com/compose/extends/"&gt;Multiple Compose Files&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By default when you run &lt;code&gt;docker-compose&lt;/code&gt;, the tool read two files from the target directory &lt;code&gt;docker-compose.yml&lt;/code&gt; and &lt;code&gt;docker-compose.override.yml&lt;/code&gt;. These two files are effectively merged to create the configuration. The thought is you'd put the common configuration into &lt;code&gt;docker-compose.yml&lt;/code&gt; and your development settings into the override.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;docker-compose.yml&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: "3.8"
services:
    nodeserver:
        build:
            context: ./home-video-server
    nginx:
        restart: always
        build:
            context: ./home-server-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;docker-compose.override.yml&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: "3.8"
services:
    nodeserver:
        volumes:
            - c:\Users\jacob\videos:/media
    nginx:
        restart: always
        ports:
            - "81:80"
        volumes:
            - ./home-video/www:/data
            - c:\Users\jacob\videos:/media
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you want to run a command using a different configuration, what you do is you explicitly declare exactly which files to use in the configuration. I created a &lt;code&gt;docker-compose.prod.yml&lt;/code&gt; with the settings for the Unraid containers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;docker-compose.prod.yml&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: "3.8"
services:
  nodeserver:
    volumes:
      - /mnt/user/kids:/media
  nginx:
    restart: always
    ports:
      - "9000:80"
    volumes:
      - /mnt/user/web:/data
      - /mnt/user/kids:/media
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the command I run when building for unraid is then&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose -f docker-compose.yml -f docker-compose.prod.yml up --build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Using Contexts to target Unraid
&lt;/h1&gt;

&lt;p&gt;Now to actually send those containers to Unraid using docker-compose.&lt;/p&gt;

&lt;p&gt;There are a number of different methods you can use, I found this nice article going over some of them: &lt;a href="https://www.docker.com/blog/how-to-deploy-on-remote-docker-hosts-with-docker-compose/"&gt;How to deploy on remote Docker hosts with docker-compose&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contexts&lt;/strong&gt; is the weapon of choice and it's fairly straightforward. First we have to create a context, which is a named connection string basically. Then we have to use that context in all our docker commands to make them execute on the target.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a context called 'remote'&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker context create remote ‐‐docker “host=ssh://user@remotemachine”
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will use SSH to connect to a 'docker endpoint' (that's the --docker part). &lt;a href="https://docs.docker.com/engine/context/working-with-contexts/"&gt;Docker Context Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For Unraid, it'll look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker context create unraid ‐‐docker “host=ssh://root@tower”
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our command from before now becomes the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose --context unraid -f docker-compose.yml -f docker-compose.prod.yml up --build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  SSH to Unraid (Use SSH Keys)
&lt;/h2&gt;

&lt;p&gt;If you tried running this you would be asked for your root password roughly a 100 times and it's likely the job would fail anyway.&lt;/p&gt;

&lt;p&gt;Because docker-compose executes so many commands over the SSH connection, it will need the password many times. We can't have that and will have to setup an SSH key.&lt;/p&gt;

&lt;p&gt;Before that though: &lt;strong&gt;SSH doesn't work on Unraid by default&lt;/strong&gt; (Access Denied error). You have to login to Unraid and give the root user a password before you can SSH. It's not enough to create a new user and give that a password, root has to have a password.&lt;/p&gt;

&lt;p&gt;What we need to do now is generate an SSH key on our Windows 10 machine. Then copy the public part of the key to Unraid.&lt;/p&gt;

&lt;p&gt;I used this guide to help me with SSH keys &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys-2"&gt;How To Set Up SSH Keys&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should already have a version of OpenSSH installed on your Windows machine in &lt;code&gt;c:\Windows\System32\OpenSSH&lt;/code&gt;. So from a command line, you can generate a key like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh-keygen -t rsa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let it install this into your user folder (&lt;code&gt;c:\users\jacob\.ssh\id_rsa&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Now we have to authorize that key on Unraid, by getting the public part of the key over to it.&lt;/p&gt;

&lt;p&gt;Because you're playing with Docker on Windows with WSL2, we can use the WSL bash to get it across to Unraid. Fire up your WSL and use this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh-copy-id -i /mnt/c/Users/jacob/.ssh/id_rsa.pub root@tower
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! ...or is it?&lt;/p&gt;

&lt;h2&gt;
  
  
  ssh-agent: agent returned different signature type
&lt;/h2&gt;

&lt;p&gt;If you try to run the docker command again, you'll have to put in your password still and you'll get this error/warning &lt;code&gt;ssh-agent: agent returned different signature type&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I found the answer and solution to this &lt;a href="https://github.com/PowerShell/Win32-OpenSSH/issues/1263#issuecomment-499542944"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It turns out the OpenSSH installation in Windows is old and incompatible. Specifically the &lt;code&gt;ssh-agent&lt;/code&gt; tool isn't good enough. So you'll have to go download another &lt;a href="https://github.com/PowerShell/Win32-OpenSSH/releases"&gt;OpenSSH binary release&lt;/a&gt; extract it to your machine and use the instructions in the link above to register the ssh-agent.exe from that location instead of the default installation.&lt;/p&gt;

&lt;p&gt;This is crappy, but it did solve the issue on my machine. A better way to do all this is probably to run all your docker stuff directly from WSL, which would have an updated SSH.&lt;/p&gt;




&lt;p&gt;Now you should be set to run your docker-compose command against the Unraid context!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose --context unraid -f docker-compose.yml -f docker-compose.prod.yml up --build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next up are tips for building and running the node.js applications.&lt;/p&gt;

</description>
      <category>unraid</category>
      <category>docker</category>
      <category>papertrail</category>
      <category>windows</category>
    </item>
    <item>
      <title>Playing with Docker on Unraid from Windows 10</title>
      <dc:creator>Jacob Korsgaard</dc:creator>
      <pubDate>Tue, 16 Mar 2021 20:22:53 +0000</pubDate>
      <link>https://dev.to/felizk/playing-with-docker-on-unraid-from-windows-10-4060</link>
      <guid>https://dev.to/felizk/playing-with-docker-on-unraid-from-windows-10-4060</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;I'm writing this series to leave a paper trail for other people to follow since I ran into a number of issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I am by no means a docker expert&lt;/strong&gt;. This is all from the point of view of a docker novice.&lt;/p&gt;

&lt;p&gt;First a few basic details about my setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's March 2021&lt;/li&gt;
&lt;li&gt;Development PC is a Windows 10 version: 10.0.19042.867&lt;/li&gt;
&lt;li&gt;Unraid is version: 6.9.1&lt;/li&gt;
&lt;li&gt;Docker Engine is version: 20.10.5&lt;/li&gt;
&lt;li&gt;I've installed WSL2 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are some links to get the basics up and running:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://wiki.unraid.net/Articles/Getting_Started"&gt;Unraid installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/windows/wsl/install-win10"&gt;WSL2 for Windows 10&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/docker-for-windows/wsl/"&gt;Docker for Windows using WSL2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;




&lt;h1&gt;
  
  
  Creating the Docker images and containers for my App
&lt;/h1&gt;

&lt;p&gt;Initially I was going to use PHP for the server - because it's what I already had - but I found this excellent:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ashwin9798.medium.com/nginx-with-docker-and-node-js-a-beginners-guide-434fe1216b6b"&gt;NGINX with Docker and Node.js — a Beginner’s guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The end product of this guide is an NGINX container that serves as a reverse proxy for a Node.js server. &lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;This guide doesn't add a frontend so I thought I'd try to do that myself and followed the documentation from NGINX: &lt;a href="https://docs.nginx.com/nginx/admin-guide/web-server/serving-static-content/#root0"&gt;NGINX Docs | Serving Static Content&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the NGINX docker container
&lt;/h2&gt;

&lt;p&gt;It only requires 2 files to setup NGINX in a container&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Dockerfile&lt;/code&gt; the instructions to create the image.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;default.conf&lt;/code&gt; the configuration of the server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's about as easy as anyone could hope for. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Dockerfile&lt;/code&gt; literally just splats the configuration onto the server and you're done.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM nginx
COPY default.conf /etc/nginx/conf.d/default.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now my &lt;code&gt;default.conf&lt;/code&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is somewhat similar to Ashwin's guide, but I'll try to explain what I'm doing here.&lt;/p&gt;

&lt;p&gt;First up &lt;code&gt;root /data;&lt;/code&gt; 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 &lt;code&gt;index.html&lt;/code&gt;) in the &lt;code&gt;/data&lt;/code&gt; folder. I'll accomplish this later by &lt;strong&gt;mounting&lt;/strong&gt; a folder on my system to the  &lt;code&gt;/data&lt;/code&gt; folder in the container.&lt;/p&gt;

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

&lt;p&gt;I did this without the &lt;code&gt;rewrite&lt;/code&gt; line first, but then my requests would all be prefixed with &lt;code&gt;/media&lt;/code&gt; so when I request &lt;code&gt;http://localhost/media/myfile.mp4&lt;/code&gt; NGINX would try to serve the file &lt;code&gt;/media/media/myfile.mp4&lt;/code&gt;. To remove the first &lt;code&gt;/media&lt;/code&gt; I used this rewrite rule. A solution I found here: &lt;a href="https://serverfault.com/questions/455799/how-to-remove-location-block-from-uri-in-nginx-configuration"&gt;serverfault | how to remove location block from $uri in nginx configuration?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, there is the &lt;code&gt;/nodeserver&lt;/code&gt; 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 &lt;code&gt;/$1&lt;/code&gt;, this won't work with just &lt;code&gt;$1&lt;/code&gt;. I found the solution to that &lt;a href="https://serverfault.com/questions/379675/nginx-reverse-proxy-url-rewrite"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Node.JS container setup
&lt;/h2&gt;

&lt;p&gt;I used the same approach as in Ashwin's guide. &lt;/p&gt;

&lt;p&gt;Here is the &lt;code&gt;Dockerfile&lt;/code&gt; I used to create my node server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:alpine
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 5000
CMD ["node", "index.js"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  docker-compose for local deployment
&lt;/h2&gt;

&lt;p&gt;I found Docker quite cumbersome before I made it to the &lt;code&gt;docker-compose&lt;/code&gt; stage. It does make it a lot easier.&lt;/p&gt;

&lt;p&gt;For a local deployment of this app, I need to spin up my two containers together and mount in a &lt;code&gt;/data&lt;/code&gt; and a &lt;code&gt;/media&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker-compose.yml&lt;/code&gt; for that looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use &lt;code&gt;docker-compose up --build&lt;/code&gt; to update the images and restart the containers.&lt;/p&gt;

&lt;p&gt;In the default.conf for the NGINX server, you may have noticed that it refers to nodeserver by &lt;code&gt;http://nodeserver:5000/&lt;/code&gt; 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 &lt;a href="https://docs.docker.com/compose/networking/"&gt;Docker Compose Documentation on Networking&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Next time I'll explain how I managed to deploy the containers to my Unraid server using SSH.&lt;/p&gt;

</description>
      <category>papertrail</category>
      <category>docker</category>
      <category>unraid</category>
      <category>windows</category>
    </item>
  </channel>
</rss>
