{"id":3063,"date":"2016-01-28T02:51:01","date_gmt":"2016-01-28T02:51:01","guid":{"rendered":"http:\/\/www.garysieling.com\/blog\/?p=3063"},"modified":"2016-01-28T02:51:01","modified_gmt":"2016-01-28T02:51:01","slug":"migrating-node-js-heroku-to-dokku","status":"publish","type":"post","link":"https:\/\/www.garysieling.com\/blog\/migrating-node-js-heroku-to-dokku\/","title":{"rendered":"Migrating Node.js apps from Heroku to Dokku"},"content":{"rendered":"<p>[av_dropcap1]H[\/av_dropcap1]eroku (and Dokku) make for an awesome development environment- the most well-known feature being the use of &#8220;git push&#8221; to deploy changes to a server. For simple projects, I&#8217;ve found that the deployment time is as fast as compilation times on JVM based projects, except that at the end you have a working environment.<\/p>\n<p>Knowing you can deploy an environment at any time during a project means you can spin up as many environments as you want later, and it forces you to think through migrations in your checkins.<\/p>\n<p>The downside of Heroku is that you are somewhat locked in, and if you want to build many small applications as a hobbyist, it adds up ($7\/mo to keep one app running &#8211; the free ones shut down all the time). On the other hand, you could just set up a VM to run several apps, which guarantees many wasted hours on dependency hell problems (every time you update a shared library, you risk breaking what you have previously built).<\/p>\n<p>[av_dropcap1]C[\/av_dropcap1]onceptually this setup is not that complex, but there are a lot of moving parts to get right, and it isn&#8217;t that satisfying to work on vs. actually building things.<\/p>\n<p>To build a system like Heroku where you can push from git, you&#8217;d start by checking a script into the repository that runs when code is pushed to one of the remote repositories (a post-commit hook). This would kick off the deployment.<\/p>\n<p>Heroku also has you check in a script to the root of your repository that defines what you want to run (a bash script with something like &#8220;node index.js&#8221; and any arguments). This lets them control the deployment, and lets the developer control what actually runs.<\/p>\n<p>[av_dropcap1]V[\/av_dropcap1]irtualization at the operating system-level saves you from dependency issues (called &#8220;Linux Containers&#8221; or &#8220;LXC&#8221;), without having to build and secure a VM for every app. Linux containers seem pretty complex and I would assume there are some good ways to screw this up from a security perspective. It is new enough that tools and best practices aren&#8217;t as mature and clear as the older generation, so it&#8217;s easy to get mired in all the options (the few hours I spent trying to get Docker working have yet to pay off)<\/p>\n<p>There is an open-source script called <a href=\"http:\/\/dokku.viewdocs.io\/dokku\/\">Dokku<\/a> (a Star Wars reference, perhaps?), which ties all these pieces together for you, and tries to act as much like Heroku as possible. Consequently you can use a cheap virtual machine, and then upgrade it when you need real infrastructure. The marketing materials for Dokku seem quite slick, so I suspect that one or more companies is sponsoring it (probably someone like Digital Ocean), although I haven&#8217;t been able to find evidence of this.<\/p>\n<p>[av_dropcap1]D[\/av_dropcap1]igital Ocean has a pre-built Dokku image you can use at $5\/mo. While they&#8217;ve had some growing pains, they typically give out credits when they have issues. If you follow them on Facebook and subscribe to their give out a lot of coupons as well, which has made the $5 VMs basically free &#8211; clearly a loss leader for them (feel free to use my <a href=\"https:\/\/m.do.co\/c\/47d5d7293b54\">referral link<\/a> &#8211; you get $10 off to start, and I get another $25)<\/p>\n<p>Regardless of who you use, if you set up a Dokku image, there is a page where you upload your SSH public key:<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-3067\" src=\"http:\/\/172.104.26.128\/wp-content\/uploads\/2016\/01\/dokku1.png\" alt=\"dokku1\" width=\"723\" height=\"674\" srcset=\"https:\/\/www.garysieling.com\/blog\/wp-content\/uploads\/2016\/01\/dokku1.png 723w, https:\/\/www.garysieling.com\/blog\/wp-content\/uploads\/2016\/01\/dokku1-300x280.png 300w\" sizes=\"(max-width: 723px) 100vw, 723px\" \/><\/p>\n<p>This lets it accepts your &#8220;git push.&#8221;<\/p>\n<p>If you want to use subdomains for each app like Heroku does, you&#8217;ll need to add them to your DNS manually. I put Cloudflare on any site I can, if only because it has the best DNS management UI I&#8217;ve ever seen (and they don&#8217;t do referral links, unfortunately!):<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-3070\" src=\"http:\/\/www.garysieling.com\/blog\/wp-content\/uploads\/2016\/01\/dokku2-1030x909.png\" alt=\"dokku2\" width=\"1030\" height=\"909\" \/><\/p>\n<p>Once you set these two things up, ssh into the VM.<\/p>\n<p>You can run create commands for each app you want to configure:<\/p>\n<pre lang=\"bash\"> dokku apps:create editor\n dokku apps:create meeting\n dokku apps:create gmail-search\n dokku apps:create document-search\n<\/pre>\n<p>This will let your VM receive pushes.<\/p>\n<p>Then, to push changes, you need to set up the remote:<\/p>\n<pre lang=\"bash\">git remote add dokku dokku@apps.garysieling.com:generic-search-ui\n<\/pre>\n<p>If you add this to an existing app, you can push to both Heroku and Dokku simultaneously by doing this:<\/p>\n<pre lang=\"bash\">git push heroku master\ngit push dokku master\n<\/pre>\n<p>You will see the same sort of commit log on the remote that you see with Heroku:<\/p>\n<pre>gary@gary-PC MINGW64 \/d\/projects\/heroku\/editor (master)\n$ git push dokku master\nCounting objects: 451, done.\nDelta compression using up to 8 threads.\nCompressing objects: 100% (350\/350), done.\nWriting objects: 100% (451\/451), 47.84 KiB | 0 bytes\/s, done.\nTotal 451 (delta 305), reused 138 (delta 91)\n-----&gt; Cleaning up...\n-----&gt; Building editor from herokuish...\n-----&gt; Setting config vars\n       CURL_CONNECT_TIMEOUT: 5\n-----&gt; Setting config vars\n       CURL_TIMEOUT: 30\n-----&gt; Adding BUILD_ENV to build environment...\n-----&gt; Node.js app detected\n\n-----&gt; Creating runtime environment\n\n       NPM_CONFIG_LOGLEVEL=error\n       NPM_CONFIG_PRODUCTION=true\n       NODE_ENV=production\n       NODE_MODULES_CACHE=true\n\n-----&gt; Installing binaries\n       engines.node (package.json):  5.5.0\n       engines.npm (package.json):   unspecified (use default)\n\n       Downloading and installing node 5.5.0...\n       Using default npm version: 3.3.12\n\n-----&gt; Restoring cache\n       Skipping cache restore (new runtime signature)\n\n-----&gt; Building dependencies\n       Pruning any extraneous modules\n       Installing node modules (package.json)\n       editor@1.0.0 \/tmp\/build\n       +-- body-parser@1.14.2\n       ...\n\n\n-----&gt; Caching build\n       Clearing previous node cache\n       Saving 2 cacheDirectories (default):\n       - node_modules\n       - bower_components (nothing to cache)\n\n-----&gt; Build succeeded!\n       +-- body-parser@1.14.2\n       +-- cookie-parser@1.4.1\n       +-- ejs@2.3.4\n       +-- errorhandler@1.4.3\n       +-- express@4.13.4\n       +-- express-session@1.13.0\n       +-- foreman@1.4.1\n       +-- http@0.0.0\n       +-- lodash@4.0.0\n       `-- morgan@1.6.1\n\n-----&gt; Discovering process types\n       Procfile declares types -&gt; web\n-----&gt; Releasing editor (dokku\/editor:latest)...\n-----&gt; Deploying editor (dokku\/editor:latest)...\n-----&gt; DOKKU_SCALE file not found in app image. Generating one based on Procfile...\n-----&gt; New DOKKU_SCALE file generated\n=====&gt; web=1\n-----&gt; Running pre-flight checks\n       For more efficient zero downtime deployments, create a file CHECKS.\n       See http:\/\/dokku.viewdocs.io\/dokku\/checks-examples.md for examples\n       CHECKS file not found in container: Running simple container check...\n-----&gt; Waiting for 10 seconds ...\n-----&gt; Default container check successful!\n=====&gt; editor container output:\n       Node app is running on port 5000\n=====&gt; end editor container output\n-----&gt; Running post-deploy\n=====&gt; renaming container (3735de61fe0a) suspicious_euclid to editor.web.1\n-----&gt; Creating new \/home\/dokku\/editor\/VHOST...\n-----&gt; Setting config vars\n       DOKKU_NGINX_PORT: 80\n-----&gt; Configuring editor.garysieling.com...\n       (using \/var\/lib\/dokku\/plugins\/available\/\n               nginx-vhosts\/templates\/nginx.conf.template)\n-----&gt; Creating http nginx.conf\n-----&gt; Running nginx-pre-reload\n       Reloading nginx\n-----&gt; Setting config vars\n       DOKKU_APP_RESTORE: 1\n=====&gt; Application deployed:\n       http:\/\/editor.garysieling.com\n\nTo dokku@apps.garysieling.com:editor\n * [new branch]      master -&gt; master\n<\/pre>\n<p>Then, if you need a database, you can install this as well:<\/p>\n<pre lang=\"bash\">sudo dokku plugin:install https:\/\/github.com\/dokku\/dokku-postgres.git\n\ndokku postgres:link editor editor\n<\/pre>\n<p>I name my database and my apps the same, which makes the above command a little unclear.<\/p>\n<p>Dokku supports the same concept of environment configuration variables that Heroku does (i.e. your database connection string, API keys, etc should never be checked in). When you run the above commands, you will get log entries like this:<\/p>\n<pre lang=\"bash\">no config vars for generic-search-ui\n-----&gt; Setting config vars\n       DATABASE_URL: postgres:\/\/postgres:baf4@dokku-editor:5432\/editor\n-----&gt; Restarting app generic-search-ui\nApp generic-search-ui has not been deployed\n<\/pre>\n<p>If you are migrating an application and change the DNS, you will need to update any references in external services &#8211; e.g. typekit, the twitter developer API, Google Developer keys, etc.<\/p>\n<p>Once you&#8217;ve set this up, there are some useful troubleshooting commands. E.g. list installed apps:<\/p>\n<pre lang=\"bash\">dokku apps\nroot@ubuntu-512mb-nyc2-01:~# dokku apps\n=====&gt; My Apps\neditor\ngeneric-search\ngeneric-search-ui\n<\/pre>\n<p>Viewing logs is a little more complex &#8211; you need to list the running processes, and get them using docker:<\/p>\n<pre lang=\"bash\">root@ubuntu-512mb-nyc2-01:~# docker ps\nCONTAINER ID        IMAGE                            COMMAND                  CREATED              STATUS              PORTS               NAMES\nfcc5604164c9        dokku\/generic-search-ui:latest   \"\/start web\"             About a minute ago   Up About a minute                       generic-search-ui.web.1\ne4e42ace32f2        postgres:9.5.0                   \"\/docker-entrypoint.s\"   About an hour ago    Up About an hour    5432\/tcp            dokku.postgres.generic-search-ui\n697e11c55807        dokku\/editor:latest              \"\/start web\"             2 days ago           Up 2 days                               editor.web.1\n\ndocker attach fcc5604164c9                                                               \n<\/pre>\n<p>And there you have it- your own PaaS, with limited dependencies on external companies for hosting.<\/p>\n<p>[av_mailchimp list=&#8217;390d68883e&#8217; double_opt_in=&#8217;aviaTBdouble_opt_in&#8217; on_send=&#8221; sent=&#8217;Thank you for subscribing to our newsletter!&#8217; link=&#8217;manually,http:\/\/&#8217; color=&#8221;]<br \/>\n[av_mailchimp_field id=&#8217;0&#8242; label=&#8217;Email Address&#8217; type=&#8217;text&#8217; value=&#8221; disabled=&#8221; check=&#8217;is_email&#8217;][\/av_mailchimp_field]<br \/>\n[av_mailchimp_field id=&#8217;av-button&#8217; label=&#8217;Subscribe&#8217; type=&#8217;button&#8217; value=&#8221; check=&#8221;][\/av_mailchimp_field]<br \/>\n[\/av_mailchimp]<\/p>\n","protected":false},"excerpt":{"rendered":"<p>[av_dropcap1]H[\/av_dropcap1]eroku (and Dokku) make for an awesome development environment- the most well-known feature being the use of &#8220;git push&#8221; to deploy changes to a server. For simple projects, I&#8217;ve found that the deployment time is as fast as compilation times on JVM based projects, except that at the end you have a working environment. Knowing &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/www.garysieling.com\/blog\/migrating-node-js-heroku-to-dokku\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Migrating Node.js apps from Heroku to Dokku&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[32],"tags":[169,173,174,270,338,350,389,464,572],"aioseo_notices":[],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/www.garysieling.com\/blog\/wp-json\/wp\/v2\/posts\/3063"}],"collection":[{"href":"https:\/\/www.garysieling.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.garysieling.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.garysieling.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.garysieling.com\/blog\/wp-json\/wp\/v2\/comments?post=3063"}],"version-history":[{"count":0,"href":"https:\/\/www.garysieling.com\/blog\/wp-json\/wp\/v2\/posts\/3063\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.garysieling.com\/blog\/wp-json\/wp\/v2\/media?parent=3063"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.garysieling.com\/blog\/wp-json\/wp\/v2\/categories?post=3063"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.garysieling.com\/blog\/wp-json\/wp\/v2\/tags?post=3063"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}