This post has been a long time coming. For about 2 years, configuration plans (Also known as wrapper plans) have been a critical part of how I use and deploy Habitat plans/artifacts in the wild. They’re a super easy way to encapsulate some required software and bundle your own configuration. For some reason, finding information about this approach has previously been difficult due to a lack of documentation.

Here’s an example of configuration plans for Habitat.

Introduction

We’ll keep this example trivial, but easy to repurpose for other plans.

Terminology:

I’ll assume you’ve already installed Habitat for the rest of this post.

Scenario: We have a super important message to post on the internet, in a static html file. And we want to host this with Nginx. Fortunately the Habitat community has an Nginx package (source code for the plan here) that we can use!

Create the plan

Create a new plan:

hab plan init mywebsite

Once that completes, change into the plan directory:

cd mywebsite

Create your content

Lets start by creating our content. Create a new file index.html in a directory called public:

mkdir -p public
echo "<h1>Hello world</h1>" > public/index.html

With our webpage ready, its time to build the configuration wrapper for Nginx.

Modify the plan.sh

Edit the plan.sh file that Habitat generated for us:

pkg_name=mywebsite
pkg_origin=grahamweldon
pkg_version="0.1.0"
pkg_maintainer="The Habitat Maintainers <humans@habitat.sh>"
pkg_license=("Apache-2.0")
pkg_deps=(core/nginx)
pkg_svc_run="nginx -c ${pkg_svc_config_path}/nginx.conf"
pkg_exports=(
  [port]=port
)
pkg_exposes=(port)
pkg_svc_user="root"
pkg_svc_group="root"

do_build() {
  return 0
}

do_install() {
  cp -r public "${pkg_prefix}"
}

This plan overrides the do_build step to do nothing, and overrides the do_install step to copy the index.html file into place in the resulting package path.

Custom Configuration

Next, create your custom Nginx nginx.conf file in config/nginx.conf

worker_processes 4;
pid {{pkg.svc_var_path}}/pid;
daemon off;
events {
  worker_connections 1024;
}
http {
  client_body_temp_path {{pkg.svc_var_path}}/client-body;
  fastcgi_temp_path     {{pkg.svc_var_path}}/fastcgi;
  proxy_temp_path       {{pkg.svc_var_path}}/proxy;
  scgi_temp_path        {{pkg.svc_var_path}}/scgi_temp_path;
  uwsgi_temp_path       {{pkg.svc_var_path}}/uwsgi;
  default_type          text/html;
  server {
    listen       {{cfg.port}};
    server_name  localhost;
    location / {
      root   {{pkg.path}}/public;
      index  index.html;
    }
  }
}

In this example, I’ve actually simplified the configuration by hardcoding some values, and removing some logic from the configuration. However you could go the other way, making the configuration include more logic/switching and tests to include more options and cover more scenarios. At this point, the configuration is yours to own and manage.

Testing

Thats it. Time to build and test!

Enter the Habitat studio, and build:

hab studio enter

build

Observations

Notice how quick that was to build? It should have taken no time at all! While writing this post, builds for me were taking 13 seconds. This includes the download time for all dependencies (Nginx, etc.)

Now you can load the service, and check it out:

hab svc load grahamweldon/mywebsite

hab pkg install --binlink core/curl
curl -vvv http://localhost

You will see the following:

# curl http://localhost
<h1>Hello world</h1>

Congratulations! You’ve just wrapped Nginx in a Habitat configuration plan!

Conclusion

Generally I feel that you should never be deploying a core plan to your servers. Core plans are maintained by the community and they are a great resource that you can depend on. However they are subject to change, and can impact your service operation.

The best scenario is to take the core plans as a dependency for binaries that you rely on for your deployment. Use the core plans configuration as a reference, but use a configuration plan implementation to construct and deploy your own configuration.

Much like a Linux distribution’s package manager, Habitat will ship default configs that work for trivial/common setups. Anything beyond that simple usecase is the responsibility of the user (you) and belongs in a configuration plan.