My son is into Skylanders and he likes to keep track of his collection. He was using the Skylanders Android app but because of a bug it no longer allows him to add Skylanders. So I ended up building him a replacement. I needed someplace to deploy it to, and having experience with DigitalOcean I figured that would be a good place. I also have experience with Docker so I thought that might be nice to use as well since I might want to run a couple other things on that server. What I felt was missing from my tool belt was a good way to deploy new code and manage my Docker containers.
After doing some googling I ended up with Dokku. Dokku is like your own personal Heroku. Deployments are done using git pushes to your server and best of all it manages your Docker images and the nginx configuration to route traffic using sub-domains.
I did run into a couple of issues but these were mostly my fault, but tracking them down took some time so I wanted to document them here.
First, and this is really a Docker best practice, is to allow configuration of your application using environment variables. Dokku allows you to set these
dokku config:set <app> KEY=VALUE and they get passed to your application. In my application, all I really needed was a data directory so
in my case I set
PORT is a special case and dokku will automatically set this environment variable when it starts up and it will
expect your web server to be running on this port.
Dokku made mapping the data directory easy as well by allowing you to pass additional arguments into your docker run using
dokku docker-options:add <app> <phase(s)> OPTION.
The phase threw me off a little in that the
run phase is not what you want. The
run phase is used when you want to run something from the command line in your docker
dokku run <app> <cmd>. What you need is to add the options to both the
Another complication I had was that I was using TypeScript and DefinitelyTyped which required some steps
to run before running the application. I tried stuffing all the commands into the
Procfile but it didn’t like running multiple commands in there. I ended up just creating
dokku.sh script in the root of my project to boot strap and run the application.
Then in the
Procfile I just called that shell script.
Finally, make sure your application listens to all interfaces. My node.js application was only listening to localhost which caused nginx to return
502 Bad Gateway.
After changing expressjs to
app.listen(process.env.PORT || 3000, '0.0.0.0', ...) it all started up.