Blog

Jon Clausen

October 26, 2016

Spread the word


Share your thoughts

With the rise of PaaS options, cloud-based and self-hosted, it's never been easier to rapidly deploy and scale apps. CommandBox now gives you a way to deploy CFML applications, using your choice of CFML engines, to the PaaS platforms Heroku and Dokku with our new Heroku Buildpack for Commandbox.

If you're not familiar with either of these PaaS implementations, click through the links above and then return here to see how to deploy your first CFML application on Heroku, using CommandBox to manage your CFML engine. A future post will discuss how to setup your own PaaS server using Dokku.

To begin, you'll need to install the Heroku command-line tools (as well as CommandBox). Mac and Windows installation instructions may be found here. Before executing the heroku commands below, you'll need to create an account and login with your Heroku CLI.

Once that's completed, lets create our first application:

mkdir dyno-might
cd dyno-might

Let's install a bare-bones Coldbox application, without database connectivity, for our example. We'll discuss the logistics of databases and shared files at the end of this post. (Note: Omit the box from the following commands if you're already in the interactive shell)

box install coldbox
box coldbox create app dyno-might

Because we're going to deploy on Heroku's hobby tier, we need to bump the heap size for our Commandbox server to fall within the 350 megabytes of memory allocated to that dyno:

box server set jvm.heapsize=300

We'll also turn on rewrites at this time for friendly URLs:

box server set web.rewrites.enable=true

Now let's start our server to ensure the app is up and running:

box server start

We should see the default Coldbox app page displayed when we open our browser:

Dyno Might - Local Example

Now that we have the files we want to deploy in place. Let's create Git repository and add those files:

git init
git add --all
git commit -m 'Initial Release'

Let's move to creating our app on Heroku. With Heroku's CLI installed simply run heroku create [APP NAME HERE], which returns the following:

$ heroku create dyno-might
Creating ⬢ dyno-might... done
https://dyno-might.herokuapp.com/ | https://git.heroku.com/dyno-might.git

Note that two URLs are returned. The first is the public URL of your app. You can configure custom domain resolution within Heroku's tools. More information on custom domains is available here. The second URL returned is a special git "repository" for the app. The repository is special because pushes to the master branch will automatically trigger a build and deployment of your application, which makes deploying updates (and rolling back) a breeze.

Grab the second URL and configure a deployment remote for your git project:

git remote add heroku https://git.heroku.com/dyno-might.git

Next we need to set the buildpack for our application to our CommandBox buildpack, so that Heroku knows how to build the application:

heroku buildpacks:add https://github.com/Ortus-Solutions/heroku-buildpack-commandbox.git

Which returns

$ heroku buildpacks:add https://github.com/Ortus-Solutions/heroku-buildpack-commandbox.git --app dyno-might
Buildpack added. Next release on dyno-might will use https://github.com/Ortus-Solutions/heroku-buildpack-commandbox.git.
Run git push heroku master to create a new release using this buildpack.

Now that we've configured our buildpack, we're ready to deploy our application to Heroku. Your branch will automatically be on master so simply run git push heroku master and you'll see that, unlike a push to a regular Git repo, the output of the push shows your deployment status from start to finish:

$ git push heroku master
Counting objects: 319, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (294/294), done.
Writing objects: 100% (319/319), 892.75 KiB | 0 bytes/s, done.
Total 319 (delta 46), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote: 
remote: -----> CFML app detected
remote: -----> Installing OpenJDK 1.8... done
remote: -----> Installing CommandBox
remote:   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
remote:                                  Dload  Upload   Total   Spent    Left  Speed
remote: 100   208    0   208    0     0   1078      0 --:--:    -- --:--:    -- --:--:    --  1083
remote: 100 38.5M  100 38.5M    0     0  21.1M      0  0:00:  01  0:00:  01 --:--:    -- 27.5M
remote: Archive:  /tmp/box.zip
remote:   inflating: /tmp/build_48db30b8a18df3e0d0b039576c9fec7f/bin/box  
remote: -----> Configuring CommandBox home: /app/.CommandBox (change with -CommandBox_home=/path/to/dir)
remote: Library path: /app/.CommandBox/lib
remote: Initializing libraries -- this will only happen once, and takes a few seconds...
remote: .........
remote: Libraries initialized
remote: CommandBox 3.3.0+00482 successfully installed
remote: -----> Configuring Container Host
remote: Set web.host = 0.0.0.0
remote: 
remote: Set openbrowser = false
remote: 
remote: -----> Host Configuration Complete
remote: -----> Commandbox Compilation Complete
remote: -----> Discovering process types
remote:        Procfile declares types     -> (none)
remote:        Default types for buildpack -> web
remote: 
remote: -----> Compressing...
remote:        Done: 87.3M
remote: -----> Launching...
remote:        Released v3
remote:        https://dyno-might.herokuapp.com/ deployed to Heroku
remote: 
remote: Verifying deploy... done.
To https://git.heroku.com/dyno-might.git
 * [new branch]      master -> master

Now you simply navigate to the URL provided when you created the heroku app (in this case http://dyno-might.herokuapp.com/) and within a matter of seconds your app will be up and running! Easy-peasy, right?

Dyno Might - Deployed

To deploy updates to your application, simply commit and push to the heroku master branch again.

Now that we've deployed our first Commandbox/Heroku app, let's briefly discuss a few other considerations:

Persistent Shared Storage

In container-based deployments, your applications are meant to be created and destroyed with each edployment. This means that user-created assets will need to be managed separately from the file system of your application, or they will be destroyed on the next deployment. This consideration necessitates the use of a CDN storage network like S3. Coldbox has an SDK available for you to use in crafting your own persistent storage solutions, or you can roll your own solution using a variety of storage providers and even your own self-hosted CDN.

Database connectivity

By default, Heroku supports the use of of PostgreSQL database connections in your applications, which you can access through your "Database" menu item in the upper right-hand corner of the Heroku Dashboard. A variety of other SQL and No-SQL addons are available, as well, to allow for persistent data storage in your applications. Below are links to a variety of different database addons:

From a deployment standpoint, you can manage your database URLs and credentials using the Heroku configuration and then pull those values in as environment variables in your Application.cfc to create the datasource like so:

heroku config:set DB_NAME='dyno_might' \
DB_USER='mighty' \
DB_PASSWORD='my53cUr3P455' \
DB_HOST='my.dbhost.com'

Then define the datasource in your Application.cfc:

var system = createObject( "java", "java.lang.System" );
var env = system.getEnv(); 
this.datasources["myDatasource"] = {
         // required
        type: 'mysql'
        , host: env[ "DB_HOST" ]
        , database: env[ "DB_NAME"]
        , port: 3306
        , username: env[ "DB_USER" ]
        , password: env[ "DB_PASSWORD" ]
};

For PostgreSQL connections, using Heroku's built-in connectivity, a $DATABASE_URL environment variable is already available. As long as you are connecting to the database from within your dyno, that connection information is already available, using a little Java to negotiate the connection string:

var system = createObject( "java", "java.lang.System" );
var env = system.getEnv(); 

this.datasources["myDatasource"] = {
	type:'postgres', 
	connectionString: env["DATABASE_HOST"]
};

Advanced Configuration and Deployment

For advanced configuration and deployment options, Heroku allows for the use of an app.json file with predeploy and postdeploy script hooks. An example schema and reference may be found here.

Deploying Alternate Branches

By default, Heroku listens for pushes to the master branch. If you would like to deploy and alternate branch, you will need to use a different syntax ending with :master for your git push command:

git push heroku development:master

### Considerations

Volume/Traffic

All files, including static assets, in your application will be served by the underlying CommandBox servlet container (RunWAR) and the assigned CFML engine. As such, deployments using the CommandBox build pack are primarily targeted towards low to medium traffic sites. For more robust deployments consider using a Tomcat/Lucee deployment or customize your own buildpack for your implementation.


#### Using Adobe CFML Engines

Because Adobe CFML Engines are closed-source and require licensing, you will need to deploy the entire ACF config directory with your application. The easiset way to do this is by setting the webConfigDir setting in commandbox to reside within the root of your project:

box server set app.webConfigDir="./"

Then commit that directory to your repo, customize your configuration and licensing and deploy that directory along with your project. The downside of this is that you add about 200-300MB of files to your repo, which will slow down your deployment times.

Another option is to set the server configuration directory, ignoring everything except the the WEB-INF/lib directory within that set of files. Then use app.json to copy that directory during predeploy, and then stop the server during post-deploy, replace that directory within the generated config Directory, and restart the server. From an ease-of implementation standpoint, shipping the entire config directory provides the most simple solution.

Feel free to test deployments with the new buildpack and post issues or pull requests as you grow your own PaaS implementation.

Happy Coding!

Add Your Comment

(3)

Jan 06, 2017 13:01:22 UTC

by Didier LESNICKI

Dear Jon, Thanks a lot for this very inresting post. I have tried to follow exactly your instructions but I always receive this: (even when I upgrade to a more powerful dyno ) May be you could help me ? Best regards, Didier Lucee 4.5.3.020 Error (expression) Message invalid component definition, can't find component [coldbox.system.Bootstrap] Stacktrace The Error Occurred in /app/Application.cfc: line 24 22: // application start 23: public boolean function onApplicationStart(){ 24: application.cbBootstrap = new coldbox.system.Bootstrap( COLDBOX_CONFIG_FILE, COLDBOX_APP_ROOT_PATH, COLDBOX_APP_KEY, COLDBOX_APP_MAPPING ); 25: application.cbBootstrap.loadColdbox(); 26: return true;

Jan 06, 2017 16:28:51 UTC

by Jon Clausen

Hi Didier, That error usually occurs because your Coldbox framework isn't deployed with the application (or it's not in the main root, in which case you'll need to create a mapping in Application.cfc). If you've omitted those files from your repository, you can add the "box install" to your "scripts.postdeploy" in "app.json". See here for more information: https://devcenter.heroku.com/articles/app-json-schema. Feel free to reply with any additional questions.

Mar 28, 2020 21:02:42 UTC

by Francesco

Hi Jon, I've tried several times using this tutorial, but always get error h10 from heroku. I've also tempted using the button here: https://github.com/Ortus-Solutions/heroku-buildpack-commandbox The app is correctly deployed, but always crash. Do you have any suggestion?

Recent Entries

Ortus Content Digest for week of August 19th

Ortus Content Digest for week of August 19th

It's August 19th... Into the Box is approaching fast, what has Ortus been publishing this week? We have the CFML News Podcast, some CFCasts and YouTube Videos, lots of Ortus and ITB Blog Posts. We have a lot more planned for next week as well.

Gavin Pickin
Gavin Pickin
August 19, 2022
Integrating ColdBox with Existing Code Series 5: Using Wirebox

Integrating ColdBox with Existing Code Series 5: Using Wirebox

Recently, I did a webinar on Refactoring Legacy Code and the question came up about whether or not it was possible to use ColdBox with existing code without converting everything to a ColdBox module or making changes to the existing codebase. In the first installation in this series, we took a tour of the various elements which make up ColdBox. In the second installation, we looked at creating layouts, views, and routes in the main site. In the third, we created a module and did the first integration of our existing code into our ColdBox site. In the fourth, we continued developing our module with a handler and passing variable back to Coldbox. Now we'll use Wirebox with and without ColdBox Modules to see how these approaches differ from a traditional approach.

Dan Card
Dan Card
August 18, 2022
Ortus Content Digest for week of August 12th

Ortus Content Digest for week of August 12th

It's August 12th... what has Ortus been publishing this week? We have the CFML News Podcast, some CFCasts and YouTube Videos, lots of Ortus and ITB Blog Posts. We have a lot more planned for next week as well.

Gavin Pickin
Gavin Pickin
August 12, 2022