Max SchmittMS
10th July 2015

How to persist folders and files with dokku and docker-options

If you want to persist folders or files for an application deployed with dokku, here is how I got it to work. To take you through the steps, we will be creating a little node app that creates a file on each startup/deploy and displays all the files created when you hit the / route.

TL;DR: If you don't enjoy the handholding, you can skip straight to the part where we mount our volumes: Initialize the app.

First, let's create a simple package.json to get us started:

package.json

{
"scripts": {
"start": "node main"
},
"engines": {
"iojs": "2.x"
}
}

Install express

Then, let's install our only dependency, express:

$ npm i express -S

Also, let's make that our folder (to which we will write our files) exists:

$ mkdir -p storage

Now for our little application:

main.js

'use strict'
const fs = require('fs')
const express = require('express')
const path = require('path')
const port = process.env.PORT || 3000
const dirName = process.env.STORAGE_DIR || path.join(__dirname, 'storage')
const app = express()
fs.writeFileSync(path.join(dirName, '' + Date.now()), '')
console.log('wrote to ' + dirName)
app.get('/', function(req, res) {
fs.readdir(dirName, function(err, files) {
if (err) {
res.status(500).end('Unkown error')
console.error(err.stack || err)
process.exit(1)
}
files.forEach(function(file) {
res.write(file + '\n')
})
res.end()
})
})
app.listen(port)

Notice that I am using an environment variable STORAGE_DIR, because we will set this in our production environment.

If you run node main and visit localhost:3000 you should see one or more newline-separated timestamps. Ok, let's get this ready for deployment with dokku.

.env

BUILDPACK_URL=https://github.com/heroku/heroku-buildpack-nodejs
STORAGE_DIR=/storage

Our .env file sets up some important environment variables. First, we'll be using heroku's node.js buildpack that lets us use io.js. Also, we're setting our STORAGE_DIR environment variable as announced previously.

.gitignore

node_modules
storage
.DS_Store

Put the usual suspects in your .gitignore (don't forget the storage folder). Now initialize git:

Initialize git

$ git init
$ git add -a
$ git commit -m "initial commit"
$ git add remote dokku dokku@dokku:persistence

Initialize the app

$ ssh dokku apps:create persistence
$ ssh dokku docker-options:add persistence "run -v /home/apps/persistence/storage:/app/storage"
$ ssh dokku docker-options:add persistence "deploy -v /home/apps/persistence/storage:/app/storage"

Note that it is not a mistake that we are specifying /app/storage to be persisted although we specified STORAGE_DIR=/storage (without /app) in our .env file.

After that is done, we are ready to deploy:

Deploy the app

$ git push dokku master

When that is done, you can run

$ ssh dokku logs persistence

to see something like this:

Detected 512 MB available memory, 512 MB limit per process (WEB_MEMORY)
Recommending WEB_CONCURRENCY=1
> @ start /app
> node main
wrote to /app/storage

Note again that the console says we are writing to /app/storage and not /storage, even though STORAGE_DIR=/storage.

Visiting the app in your browser will give you a single timestamp.

Making sure everything works

First, you will want to see if your options are set correctly:

$ ssh dokku docker-options persistence

You should see something like this:

Deploy options:
-v /home/apps/persistence/storage:/app/storage
Run options:
-v /home/apps/persistence/storage:/app/storage

Next, you can ssh into your machine, look at the persisted directory

$ ls /home/apps/persistence/storage/

and you should see a single file with a timestamp as a name.

Ok, everything looks good. Now let's rebuild our app and see if we get a second timestamp added while keeping the first one we created:

$ ssh dokku ps:rebuild persistence

When the build is done, you should be able to revisit your site and see that a second timestamp was added. It works!

Destroying persisted apps

Please be aware that when you destroy your app by running

$ ssh dokku apps:destroy persistence

the persisted folders will stay on the remote system and you will need to delete them manually if you want them gone.

Image of my head

About the author

Hi, I’m Max! I'm a fullstack JavaScript developer living in Berlin.

When I’m not working on one of my personal projects, writing blog posts or making YouTube videos, I help my clients bring their ideas to life as a freelance web developer.

If you need help on a project, please reach out and let's work together.

To stay updated with new blog posts, follow me on Twitter or subscribe to my RSS feed.