Skip to content

Plugin creation

A plugin can be a simple implementation of triggers or can implement a command structure of its own. Dokku has no restrictions on the language in which a plugin is implemented; it only cares that the plugin implements the appropriate commands or triggers for the API. NOTE: any file that implements triggers or uses the command API must be executable.

If you create your own plugin:

  1. Take a look at the plugins shipped with Dokku and hack away!
  2. Check out the list of triggers your plugin can implement
  3. Upload your plugin to GitHub with a repository name following the dokku-<name> convention (e.g. dokku-mariadb)
  4. Edit this page and add a link to your plugin
  5. Subscribe to the dokku development blog to be notified about API changes and releases

Compilable plugins (Golang, Java(?), C, etc.)

When developing a plugin, you must implement the install trigger such that it outputs the built executable(s) using a directory structure that implements the plugin's desired command and/or triggers the API. See the smoke-test-plugin for an example.

Command API

There are 3 main integration points: commands, subcommands/default, and subcommands/<command-name>.

commands

Primarily used to supply the plugin's usage/help output. (i.e. plugin help).

subcommands/default

Implements the plugin's default command behavior. (i.e. dokku plugin).

subcommands/<command-name>

Implements the additional command interface and will translate to dokku plugin:cmd on the command line. (i.e. dokku plugin:install).

Sample plugin

The below plugin is a dummy dokku hello plugin.

hello/subcommands/default

#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"

hello_main_cmd() {
  declare desc="prints Hello \$APP"
  local cmd="hello"
  # Support --app/$DOKKU_APP_NAME flag
  # Use the following lines to reorder args into "$cmd $DOKKU_APP_NAME $@""
  local argv=("$@")
  [[ ${argv[0]} == "$cmd" ]] && shift 1
  [[ -n $DOKKU_APP_NAME ]] && set -- $DOKKU_APP_NAME $@
  set -- $cmd $@
  ##

  [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on"
  verify_app_name "$2"
  local APP="$2";

  echo "Hello $APP"
}

hello_main_cmd "$@"

hello/subcommands/world

#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"

hello_world_cmd() {
  declare desc="prints Hello World"
  local cmd="hello:world"
  # Support --app/$DOKKU_APP_NAME flag
  # Use the following lines to reorder args into "$cmd $DOKKU_APP_NAME $@""
  local argv=("$@")
  [[ ${argv[0]} == "$cmd" ]] && shift 1
  [[ -n $DOKKU_APP_NAME ]] && set -- $DOKKU_APP_NAME $@
  set -- $cmd $@
  ##

  [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on"
  verify_app_name "$2"
  local APP="$2";

  echo "Hello world"
}

hello_world_cmd "$@"

hello/commands

#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x

case "$1" in
  help | hello:help)
    help_content_func () {
      declare desc="return help_content string"
      cat<<help_content
    hello <app>, Says "Hello <app>"
    hello:world, Says "Hello world"
help_content
    }

    if [[ $1 = "hello:help" ]] ; then
        echo -e 'Usage: dokku hello[:world] [<app>]'
        echo ''
        echo 'Say Hello World.'
        echo ''
        echo 'Example:'
        echo ''
        echo '$ dokku hello:world'
        echo 'Hello world'
        echo ''
        echo 'Additional commands:'
        help_content_func | sort | column -c2 -t -s,
    else
        help_content_func
    fi
    ;;

  *)
    exit $DOKKU_NOT_IMPLEMENTED_EXIT
    ;;

esac

Each plugin requires a plugin.toml descriptor file with the following required fields:

[plugin]
description = "dokku hello plugin"
version = "0.1.0"
[plugin.config]

A few notes:

  • Remember to chmod +x your executable files
  • Always support DOKKU_TRACE as per the 2nd line of the above example
  • If your command depends on an application, include a check for whether that application exists (see the above example)
  • You must implement a help command, though you may leave it empty. Also, you must use commas (,) in the command syntax to support output in columns
  • Commands should be namespaced
  • As of 0.3.3, a catch-all should be implemented that exits with a DOKKU_NOT_IMPLEMENTED_EXIT code. This allows Dokku to output a command not found message.
  • Consider whether you want to include the set -eo pipefail option. Look at the following example :

    IMAGE=$(docker images | grep "user/repo" | awk '{print $3}')
    if [[ -z $IMAGE ]]; then
        dokku_log_fail "user/repo image not found... Did you run 'dokku plugin:install'?"
    fi
    

If user/repo doesn't exist, Dokku exits just before the awk command and the dokku_log_fail message will never go to STDOUT. printed with echo. You would want to use set -e in this case.

Here is the help entry for set:

help set
  Options:
    -e  Exit immediately if a command exits with a non-zero status.
    -o option-name
        pipefail     the return value of a pipeline is the status of
                     the last command to exit with a non-zero status,
                     or zero if no command exited with a non-zero status
- In the case that your plugin needs to set application configuration settings and you want to avoid having to restart (default Heroku-style behavior) these "internal" commands provide this functionality:

dokku config:set --no-restart node-js-app KEY1=VALUE1 [KEY2=VALUE2 ...]
dokku config:unset --no-restart node-js-app KEY1 [KEY2 ...]
- If you want to allow other plugins access to (some of) your plugin's functionality, you can expose this by including a functions file in your plugin for others to source - You should consider all functions in that file to be publicly accessible by other plugins - Any functions you want to keep private should reside in your plugin's trigger/ or commands/ directories - As of 0.4.0, Dokku allows image tagging and deployment of tagged images - This means hard-coding the $IMAGE as dokku/$APP is no longer sufficient - You should now use get_running_image_tag() and get_app_image_name() as sourced from common/functions (see the plugin triggers doc for examples). Note: This is only for plugins that are not pre/post-build-* plugins - As of 0.5.0, we use container labels to help cleanup intermediate containers with dokku cleanup - This means that if you manually calldocker run, you should include$DOKKU_GLOBAL_RUN_ARGSto ensure your intermediate containers are labeled correctly - As of 0.6.0, you should not **not** call thedokkubinary directly from within plugins because clients using the--appargument are potentially broken when doing so (as well as other issues) - You should instead source thefunctions` file for a given plugin when attempting to call Dokku internal functions