An API is a Competitive Advantage

In this increasingly inter-connected world, APIs are becoming more and more important as time goes on. This is especially true if you have a business that requires integration of some sort, like metrics, notifications, integrated access to other systems (like telephony), payments, etc.

Companies like Stripe, Amazon, and Twilio have embraced the API-first approach, and in many ways embody and epitomize this movement as a whole.

Beyond Just Having An API

Just having an API is the obvious requirement for basic integrations. Going further than that, however, is the thought that your API can actually be a key point of differentiation from your competitors. Using this strategy (creating a robust, easy-to-use API) can be especially effective when you are going up against entrenched competitors, or when you are trying to make something that is traditionally very hard, easy.

Stripe And The Payments Industry

Ask any developer about online payment gateways, and they are likely to mention Stripe. Why? Because it was clear from the start that they really cared about devleopers, and put high priority in their API. Not only just creating an API – because every online payment system has an API – but in creating a very good API that is robust, simple, well-documented, and easy to use.

In contrast, many of Stripe’s competitors are using SOAP APIs or an emulation of the API. The API documentation typically exists only in PDF form, and it’s something that is mailed to you by the sales department. You’re lucky if you can find it on the website. Sales first and developers second is pretty much the exact opposite approach that Stripe took by focusing on developers and integrations first.

Here an example from the Stripe documentation – it’s just a simple cURL call to charge a card, and returns a simple JSON response:

curl \
    -u sk_test_BQokikJOvBiI2HlWgH4olfQ2: \
    -d amount=400 \
    -d currency=usd \
    -d card=tok_14i9vP2eZvKYlo2Cdr4h0oHs \
    -d "description=Charge for"

Stripe did several things right here:

  • Provide a simple API with good documentation
  • Provide a fast on-boarding process with no red tape (rare for credit card processors)
  • Support subscription charges with no additional fees (also rare)
  • Target and market to developers
  • Good design and nice, clean merchant interface

Stripe’s success is a combination of the above reasons as well as many other factors, but without a doubt their core product and main competitive advantage is their API. It shows in their overall developer experience, and has played a large role in their success in stealing market share from entrenched competitors like

Amazon and the Public Cloud

For many, Amazon is synonymous with cloud computing. Many web hosts selling vitrualized servers came and went before Amazon got into the game, but no one besides maybe DigitalOcean has had a similar level of success doing so.

From the start of Amazon Web Services, Amazon made it clear that they were a platform for developers to build on top of, and provided an API from day one. So while many other virtual hosting providers existed, Amazon EC2 was one of the only ones that developers could use to provision whole new servers with automated scripts and zero manual intervention. The availability of APIs to provision servers lead to the creation of businesses built on top of Amazon’s infrastructure, like Heroku – who probably wouldn’t exist without Amazon’s APIs.

Rackspace, a much larger web host and significant competitor, didn’t launch a public API until years after Amazon did, but it was already too late, and they gave up significant market share to Amazon and Google Cloud Engine. Amazon’s API was its killer feature, and key differentiator. And we all know how well that has gone for them.

Twilio And Telecommunications

Twilio is a good example of a company using APIs to make something that is normally really difficult very easy. Now you don’t have to worry about which cell phone network the number you are texting belongs to, what country it is in, etc. Just integrate with the Twilio API, and you know it’s going to work.

For Twilio, their API is their entire business. There is no Twilio without an API, because if Twilio was just a web form that sent a text message to any given number – even if it still smoothed over all the carrier and location differences – it would not acheive the goal of automation, and thus would defeat the purpose.

In the years since Twilio launched, countless companies have relied on it for things like 2-factor authentication and phone number verification via SMS. Twilio can even power your entire phone system through tools like OpenVBX, all with a collection of REST APIs.

The Bottom Line

If you don’t have an open REST API that is easy to use, you will lose market share to a competitor who does. It’s time to start taking your API very seriously. An API is a competitive advantage.

Hiding Variables From PHP’s var_dump()

NOTE: This post is irrelevant for PHP 5.6+: a new __debugInfo() magic method has made its way into PHP 5.6, so use that as the best solution to this issue.

A common problem I have in PHP is that when I deal with larger objects with multiple dependencies or circular references, var_dump() becomes effectively useless. Often times it spits out huge numbers of objects that were set as a dependency on the object I wanted to inspect that I didn’t even know were there. Even worse, there are often circular references and other things that get in the way. The signal-to-noise ratio is way off the charts.

Playing Hide-and-seek with var_dump()

So is there a way to effectively “hide” a variable from functions like var_dump, print_r, and var_export? Turns out, there is – but it does come with some serious trade-offs.

The first thing you need to know is that there is no way to hide an object property. Object properties will all be dumped, no matter the scope – public, protected, or private.

The trick (or hack, if you will) relies on using a very handy but little-used feature of PHP: static variables. No, not static class properties – static variables. You may have seen them, or even used them yourself a time or two:

 function connection() {
     static $connection;
     if ($connection === null) {
         $connection = new DatabaseConnection();
     return $connection;

Basically our connection function acts as a kind of factory. The $connection variable here is cached between function calls so that the object is only ever created once, and then simply returned on subsequent calls. The static declaration before our variable tells PHP that this variable will maintain its value within the current execution scope, much like a static class property maintains its value across multiple object creations.

That’s Great, But Now Your Dependecy Is Hard-Coded

The more experienced coders among you may have noticed that using this approach, we can no longer use constructor dependency injecton. At least – not exactly like we used to.

Remember when I said solving this problem comes with some serious trade-offs? Okay, stay with me here. We can still use dependency injection.

The main design trade-off you will have to make if you want to use this approach is that you will have to create a single method that is both a getter and setter for each object dependecy you want to inject. This is best illustrated by example:

 function connection(DatabaseConnection $newConnection = null) {
     static $connection;
     if ($newConnection !== null) {
         $connection = $newConnection;
     return $connection;

Okay, so now we have modified our function to be both a setter and a getter. We can now use this function to set our dependecy upon object creation, and to get the connection we need to use in place of the typical object property.

Using Dependency Injection In An Object

So now, instead of setting an object property and using that object property for our dependency like so:

 class DoStuff {
     protected $connection;

     public function __construct(DatabaseConnection $connection)
         $this->connection = $connection;

     public function someQuery()
         $this->connection->query("SELECT ... blah blah blah");

We can remove the object property and replace it with our new method, and use our connection method as a setter in the constructor (while still using dependecy injection), and then use our connection method anywhere we need it as a getter.

 class DoStuff {
     public function __construct(DatabaseConnection $connection)
         // Setter

     public function connection(DatabaseConnection $newConnection = null)
         static $connection;
         if ($newConnection !== null) {
             $connection = $newConnection;
         return $connection;

     public function someQuery()
         // Getter
         $this->connection()->query("SELECT ... blah blah blah");

And there we have it. We get the same functionality with a little bit more code, except now our dependent object is completely hidden from var_dump, because it is not an object property. It’s just a variable inside a method, which no PHP inspector function will display.

Benefits To This Approach

  1. The dependency is completely hidden from var_dump, var_export and print_r. This results in nice and clean debugging dumps without any large dependency graphs or circular references.
  2. Your database and service credentials are safe. Related to #1 – this is such a huge benefit by itself that is has to have its own point. How many times have you dumped the contents of an object only to see a database connection dependency or API service dependency also dump out all your connection information, usernames, passwords, and API keys? Hopefully this never happens in production, but even in development, this is irritating.
  3. Save time and hassle. Sometimes dumping an object’s contents can be unexpectedly gigantic. In these cases, most of your time spent debugging is scrolling through pages and pages of junk you don’t need to find the one piece of information you’re actually looking for. This can be very frustrating, especially if you’re in an edit-debug cycle where you are repeating this multiple times in quick succession.

Drawbacks To This Approach

So… trade-offs, right? Here are the main ones I have seen:

  1. You have to use a combined getter/setter method. This is because the static variable’s scope only exists inside the method it is defined in. I personally like single-word combined getter/setter methods, because they simplify and reduce the surface area of your object’s API, so this may not even be a drawback to you.
  2. The object reference will now be static. This is probably okay for most objects you pass that are setup once and remain constant, like a DatabaseConnection object would be, but it won’t be okay for other scenarios, like a collection of result objects or anything else that may change for each new object instance you want to create.

    (Update: I previously said the object reference won’t change (mutate) anymore once set with this method, but my tests on that were flawed.)

  3. The code might be confusing. Simplicity has a lot of value. This approach does add a little bit of complexity to the code, and may be a little harder to understand and debug, especially for beginners. It’s likely that people will see this code and say something like “Why don’t you just use an object property?”, because what it is doing is not immediately obvious.

If you are using this approach and can think of any other benefits or drawbacks, let me know and I will update this post. Hiding variables from var_dump and other similar functions is something I really wish was built-in PHP.

UPDATE: I have been informed that a new __debugInfo() magic method has made its way into PHP 5.6 – this is exactly what I was looking for, so now you just have to upgrade when PHP 5.6 has a stable release 🙂

Titanium Proxy Objects With JavaScript Call and Apply

Sometimes language and platform abstractions bite you. They can seem pretty straightforward and “just like the real thing”, but sometimes they have odd behaviors that don’t quite work the way you expect.

Dynamic method invocation with .call() and .apply()

The .call() and .apply() functions are something you will use a lot if your code involves a lot of dynamic arguments, such as building an array of arguments that then need to be mapped to actual ordered function arguments in a function or method call.

Since Titanium runs native JavaScript with the V8 engine bundled in your app, you will naturally find yourself attempting things like this:

var args = [sql].concat(params);
var db ='dbname');
var rs = db.execute.apply(db, args);

But if you run that, and you will be presented with the head-scratching error:

invalid method 'execute:'

Although the error is pretty confusing – you know there is a method named execute on the db object – there actually is a good explanation for this behavior.

Titanium Proxy Objects

What intiutively seems to you like a normal JavaScript variable here – db – is actually a proxy object created and returned by Titanium with the call. Titanium also returns proxy objects for pretty much every other component that has a native counterpart they are bridging over to behind the scenes. This black magic is fundamental to how Titanium works internally.

The Solution

Although the main Titanium proxy object explanation suggests creating a wrapper object for the method call, that’s actually not necessary at all, and creates a lot of extra work, as well as many additional lines of code.

The best solution is to use a bit of JavaScript meta programming and call the apply method directly from the Function prototype:

var args = [sql].concat(params);
var db ='dbname');
var rs =, db, args);

This is a nice one-liner that achieves the same end result, is much easier to undertand, and does not require a wrapper object or another layer of indirection to achieve.

Future Anticipated Questions

I was recently part of a project where, upon asking for content for the requried “Frequently Asked Questions” page, I was invited to a meeting with the objective of “brainstorming content for the F.A.Q. page”. It was painfully ironic. How can we possibly list frequently asked questions when we haven’t even launched yet and no one has asked any questions?

The meeting (video conference call) started, and I decided to stay silent for a little while to see how the call would play out. The project stakeholders begun by going over the F.A.Q. sections on a bunch of other websites, and compiling a short list of all the questions they would need to write answers for. The list included things like “How can I contact you”, and “How can I reset my password”questions that will likely never be asked unless your UI/UX is terrible. Continue reading

Pulling My Apps From The App Store: Lessons Learned


Last night, I decided to pull my two iOS and Android apps SEMTab SEO Pro and my very first app, AutoRidge Lite from the App Store and Google Play. It was a difficult decision to make, because they were my first two apps, and I now have no published mobile apps to show potential freelance clients when they ask for app examples. This does limit my options, but now is a great time to make this move since I just started a full-time long-term contract and won’t be actively looking for extra work right now.

Showcasing Quality

With the recent shutdown of my company, I feel like I’m writing a new chapter in my life. Having any app in the app store just to say I have one is no longer enough. The bar is higher now. I want a high-quality, well-rated app that people genuinely like and want to use. I want something I can really be proud of. It doesn’t have to be complicated or difficult, or even do anything fancy. It just has to look good, and do one thing well — and not crash randomly. Difficult task, I know.

What A Bad App Looks Like

To provide an example of the kind of results a bad quality app will give you, look no further than my own app – my only paid app – one I just pulled from the stores – SEMTab SEO Pro. This app was ill-conceived and hastily crafted. The UI wasn’t thought out very well, and neither was the stability of the source data. The code I used to fetch Google’s PR ranking was frequently wrong (I apparently used the wrong black magic incantation to summon the accurate result), and link counts from Facebook and Twitter were different than what were reported by other (more accurate) tools and by the platforms themselves (turns out the API I used was way outdated).


On Google Play, SEMTab had an abysmal rating of 1.6. This is primarily due to the app crashing or just plain not working on a few Android devices that I was not able to test on, or were not actually supported (like the “Rubbish” review you see for it not working on the Galaxy Tab – one of the first Android tablets that liked to pretend it could run normal Android apps just fine).


On the App Store, the app was a lot more stable, but still got a bad rating for its terrible accuracy.



The app was $1.99, and was on sale for $0.99 for an extended period of time to try and boost sales. Obviously, this strategy doesn’t work if your app sucks and has a bunch of bad ratings.

Google Play Revenue


App Store Revenue


A whopping $127.19 from Google and $83.01 from Apple, for a grand total of $210.20. In 4 years. Ouch. Totally not worth the (admittedly small) effort I put into it.

Why I Pulled SEMTab SEO Pro

The reasons here are prety obvious, right? At this point, the ratings and performance of this app are so bad that I don’t even want it in my portfolio. I didn’t even want people to know I made this app, and was a bit embarrased to mention it whenever the “what apps have you made?” question came up, fearful people would see the terrible rating read all the horrible reviews. Good riddance. (And if you’re wondering why I am publicly sharing all this now, it’s for accountability – I never want to make an app this bad again.)

What a Mediocre App Looks Like

I am definitely more proud (or, at least, not embarrased) by my first app – Autoridge. I was a lot more torn about taking down Autoridge than I was about SEMTab. The reviews of Autoridge basically boil down to this:

  • The people who used the app and found relevant data for their vehicle’s year/make/model loved it. It provided huge value for them.
  • The people who could not find their specific vehicle’s year/make/model, or didn’t find any (or very few) relevant parts for their vehicle mostly trashed it as incomplete, inaccurate or unreliable.

Poor Early Titanium Android Support

And as in the case of SEMTab, there were also quite a few Android users that the app didn’t work for at all (freezing or crashing). I mostly attribute this to the very poor early Appcelerator Titanium Android support. Both these apps were developed and released at least 3 years ago on Titanium versions 1.5.x – 1.7.x, and a lot has changed for the better since those dark ages with Titanium (we have have an MVC framework called Alloy, and Titanium is at 3.2.x). From my experience with Alloy, I am positive that updated versions of these apps would elimate all the problems on Android.

That said, it’s pretty disharenting when you develop an Android app with an intermediate platform like Titanium, test it locally with your own Android device and emulator working perfectly, and then release it to a tsunami of 1-star reviews and reports of random freezes and crashes on various Android devices that you can’t do anything to fix.


On Google Play, Autoridge Lite had a very average rating of 3.1. I am actually suprised it was this high with the number of ratings reporting freezes and crashes, but like I said, the number of people who it did work for generally loved it. You can see this wide rating distribution reflected in the data. This saddens me a bit, because the rating could have easily been 4+ in Google Play if it worked consistently across all Android devices.


The App Store rating was pretty much the same story, with all the negative reviews questioning data completeness and accuracy:



The uptake of Autoridge Lite surprised me from the start. It definitely seems like something people were ready to embrace if the data was more accurate, complete, and up-to-date. I think overall, people really like the idea of using an app instead of flipping through a greasy old parts book at the auto part store.

App Store Distribution Units


Google Play Installs


According to Google, there are still 1,584 devices with Autoridge Lite installed. Definitely far better than SEMTab SEO Pro that had only 12 active installs. A lot of people still like this app.

Why I Pulled Autoridge Lite

The decision to pull Autoridge from the App Store and Google Play was much harder than with SEMTab, but ultimately, it came down to an issue of time, energy, and focus:

  • Autoridge Lite has a webservice backend that I also built and maintain, and this costs time and money.
  • The vehicle data has a low degree of accuracy with no automatic error correction, and would take a significant re-investment of time and energy to correct. I would have to research new sources of auto part information and build web scrapers/parsers for each of them, as well as implement better processes for checking for updates and invalidating bad data.
  • I want to completely re-code the app, and have wanted to for a while. So doing this plus all the webservice updates too would probably take longer than the original 2 weeks the entire project took initially. And since I can’t update the app only without fixing the data accuracy issue, I have to consider the whole picture.

In short – it would take too long to re-build the right way, and I don’t currently see it as worth the effort considering I haven’t made any money from it (and don’t see any path to make significant money from it ever as a one-man show).


Although this choice was tough, and arguably should have happened a long time ago, I am happy to be doing it now. The first step towards finding something that works is identifying and eliminating the stuff that doesn’t. The worst thing that can happen is to split your time, energy, and focus across so many projects that you do them all badly. This is exactly what I feel I have done. I want to be able to focus on my next project, I want it to be high-quality, and I want it to have a clear revenue path. This whole “making money with software” path is not an easy one, so I want to give myself the best chance possible by concentrating on the next big thing, and making a better product than I’ve ever made before. That’s what this move is all about.


After running Brightbit with Joshua Ogle and Eric Boehs for nearly fours years, it officially came to an end a few weeks ago on February 28, 2014 when we closed the office. It’s a long story, and there is no blog post for it yet, but now is not the time. Long story short, we worked too far past our deposits for a few clients who used to be awesome clients, but, as it turns out, couldn’t pay anymore. We ran out of money, had to lay everyone off, and close the office. It was the most depressing experience of my life – but like I said, that’s for another post some other time.Brightbit Has Closed Message

What do You do After Running Your Own Company?

This is a question I’ve been asking myself a lot over the past few months, facing the impending office closure and winding down of my own business. Thing is, when you’re a business owner, you get to (read: have to) wear a lot of different hats. On any given day, I’d do development, marketing, sales, project management, HR, project planning, business planning, financial forcasting, and more. Since your role isn’t really defined, it just expands to fill whatever role is necessary at any given time. It’s both frustrating and liberating, and it’s an incredible learning experience – and unlike anything else that any full-time job is going to give you. This makes choosing my next job very hard. A lot more thought goes into what I am going to go with the next few years of my life, and how that will help me along my path, whether or not it will be interesting and challenging enough for me, etc.

What I am Looking For Now

After taking a little breather to do some hard thinking and evaluate my options, I have come to the conclusion that… I have no idea what I really want to do next. I am open to – and actively evaluating – a lot of different options right now ranging from part-time and full-time contracts to more traditional (and even some quite unique) full-time jobs. One thing I do know, however, is that it does have to be something that is both very interesting and challenging for me – something that will force me to grow and learn. It also has to be either remote/telecommute or based in the OKC area. I have put down significant roots in OKC, am very involved in the local developer community, and have lots of family here. Basically, I really don’t want to move right now (unless maybe you need me to relocate to say… the Bahamas or the Virgin Islands and pay generous housing allowances. There’s a chance I might go for that.)

Have work for me? Get in touch. No recruiters, please!

UPDATE: I have accepted and started a full-time 12-month contract, so I am no longer looking for work. Thanks for all the support and emails!

MacBook Pro Retina 3.5mm TRRS Cable With Audio Input

One of the unexpected surprises that came with upgrading to a new Macbook Pro Retina 15″ from an older MacBook Pro was Apple’s inexplicable deletion of the audio input (mic) 3.5mm jack. I use a regularly use a gaming headset with a boom mic that was now rendered completely useless, because I was no longer able to plug in the mic. I love it that Apple’s hardware is on the cutting edge, but sometimes they do things like this that just don’t make sense for the average user.

What Happened?

I did some searching around, and discovered that Apple changed the standard dual input/output 3.5mm jacks for a single combined TRRS 3.5mm jack with 3 contacts – the same one they use for the iPod, iPhone, and iPad. I guess it makes sense for Apple to standardize everything so you can use their headphones everywhere, but it caught me offguard and really frustrated me that I would have to buy something extra just to continue using my headset.

Where did the mic jack go!?

Things I Tried

I did some searching and originally purchased this Headset Buddy cable from Amazon. It’s $15.95(!) and has branding from the 1980’s. I cringed and made the purchase thinking it would be worth it if it solved my problem. When it arrived, it was clear that the price of the cable is about 3x what it should be. The cable is flimsy and cheap, and didn’t even work at all. I promptly returned it. The customer reviews are mixed, but don’t gamble on it.

That cable purchase has soured my view on the TRRS splitters for a while, so I opted for a USB audio adapter to 3.5mm input/output jacks instead. It was a little cheaper at $10.95 and worked great, but came with a huge downside: It uses one of only TWO total USB ports on your MacBook Pro – a precious resource that I was already using (and thus had to also use a USB hub). Another downside here is that you have to explicitly switch to USB audio using the Sound preference pane, which can be annoying at times.

The Best Solution

The best working solution (and also the cheapest) I found is this TRRS audio cable from Monoprice for about $5. When it came in the mail, I originally thought I ordered the wrong cable, because the configuration is weird. Instead of a typical “Y” configuration off the male jack, it instead splits off from the 3.5mm audio input female jack into another 3.5mm audio output female jack. Make sure you look at all the product images to see what I mean.

Weird Cable Configuration

The cable works well and feels very durable, even if it does looks like the wrong type of cable (Monoprice, if you’re listening – please fix this. It works, but it just looks weird with the “Y” coming off the female 3.5mm input jack).


Hopefully my misadventures in trying to solve this problem that Apple itself doesn’t even have a cable for will help you not have to do all this experimentation yourself. I feel like this should have been way easier than it was to find a cable that works on newer Macbook Pros, but apparently marketing lags a bit behind for 3rd party accessories like this (all these cables are marketed as iPod/iPhone/iPad cables, which have the same TRRS 3.5mm jack). In another year or two, I doubt this will be a problem at all, but just incase it still is – you can always find this blog post :).

UPDATE (March 13, 2017): At some point after about a year and a half, the TRRS cable I recommend in this article from Monoprice stopped working. I ended up just buying and using a new USB gaming headset instead. I know that’s not the conclusion you want to hear, but it is the unfortunate truth. In my particular setup with an external monitor with built-in USB hub it is not too much of an issue since it doesn’t use an extra USB port that way, but your mileage (and USB port utilization) may vary.

Oklahoma PHP User Group Reboot

Our first Oklahoma PHP User Group meeting in a few years happened on Thursday, February 20th 2014, thanks to the efforts of Jake A. Smith who contacted me about it (as I ran the previous PHPUG a few years ago) and did all the legwork to make it happen. We are now going to co-organize the user group going forward.

After a meet & greet fueled by pizza and soda, I presented a short introductory talk on Composer, which also covered using and publishing packages to Packagist. The turnout was fairly small – around 12 people or so, but everyone was excited that the user group was happening, and there were lots of beginners and new faces.

Our meetings are going to be on the 3rd Thursday of each month. All our meeting times, descriptions, etc. will be posted on our Meetup Page. Our March plans are tentative, but we plan to do a PHP Foundations talk that will cover all the basics like Namespaces, PHP-FIG standards, code organization, etc. so it should be a great meeting for begineers and those who are not familiar with what the larger PHP community is doing. You can RSVP on the Meetup page to get notifications and reminder emails for upcoming meetings. Hope to see you there!

Using Dokku to Deploy PHP Applications with a “git push” on DigitalOcean

Want a Platform-as-a-Service setup like Heroku on your own $5/month VPS from DigitalOcean? Look no further than Dokku

  • a set of scripts built on Docker and Heroku’s own buildpacks. After this setup, you’re just one git push away from deploying your app to your own server.

Step 1: Create a new Droplet with Dokku

DigitalOcean has a great guide on how to use the DigitalOcean Dokku Application, so there is no sense in repeating the steps here. Follow the steps in that article, and then come back here. There are issues I ran into after the Dokku setup that are important steps not to skip. So be sure to come back here before trying to deploy your PHP application.

Step 2: Setup Swap Space

DigitalOcean boxes don’t come with any disk swap space configured by default, but Dokku uses some when deploying your apps, so you need to configure some using this guide before your first git push or you may run into errors (I sure did). The guide says Ubuntu 12.04, but it works on 13.04 too, so no worries. Do this and then come back. I’ll wait.

Step 3: Deploy Your App

Although the setup is not fully complete, try and deploy your app now to make sure your dokku setup is working properly. You also need to deploy your app now so you will have a container ready for the next steps.

git remote add dokku
git push dokku master

The ouput of the git push will let you know if your deploy was successful or not. If you have a successful deploy, but get a “502 Bad Gateway” response from Nginx, continue with the steps below.

Step 4: Install the user-env-compile Plugin

Install the user-env-compile plugin. This will allow you to set environment variables on your app that are available to be used at build time. This is important for the next step.

Step 5: Use CHH’s PHP Buildpack

Now that you have the user-env-compile plugin installed and have created your app’s initial container, you can set environment variable configuration values on it. To specify what buildpack you want to use, set the BUILDPACK_URL ENV value.

ssh dokku@<> config:set <app_name> BUILDPACK_URL=

You should see output like this:

-----> Setting config vars and restarting <app_name> BUILDPACK_URL:
-----> Releasing <app_name> ...
-----> Release complete!
-----> Deploying <app_name> ...
-----> Deploy complete!

NOTE: ANY Heroku buildpack will work, so if you want to use another one, you are free to do so, although it is very doubtful you will find a better one for PHP :).

Step 6: Set your Document Root

Now open your composer.json file, and add the extra configuration block to specify your document-root and index-document so the PHP buildpack will know where to serve your files from.

     "require": {
         "php": ">=5.4.0",
         "vlucas/bulletphp": "~1.3.x"
     "extra": {
         "heroku": {
             "framework": "slim",
             "document-root": "web",
             "index-document": "index.php"

Note: The framework key here is slim. This doesn’t match my framework Bullet, but I have to put this in here because this maps to the nginx config file used. So if I don’t specify this here, the buildpack assumes a standard classic PHP setup and will only execute .php files, and not rewrite all requests to the main index.php file like most frameworks (including Bullet) require, causing nginx to throw lots of “404 error” responses.

The configuration key says heroku, but this works for dokku too – remember Dokku basically uses all the same basic things that Heroku does – buildpacks, git based deploys, putting apps in their own sandboxed containers, etc.

Step 7: Install Other Plugins Your App Needs

Custom Domains

You will want the dokku domains plugin so your app can use domains instead of subdomains or ports.

Then add your domain to your your app using:

ssh dokku@<> domains:set <app_name>

MySQL / MariaDB

If your app uses MySQL, install the dokku MariaDB plugin (drop-in MySQL Replacement).

Then create a database for your app using:

ssh dokku@<> mariadb:create <app_name>

This will create a DATABASE_URL environment variable that will be available as a DSN string for your app to use to connect to the database via PDO or other ORM that you may want to use. You can access this in PHP via $_SERVER['DATABASE_URL'].

Other Plugins

If you use Redis, Memcached, or anything else, check the Dokku Plugins page and install whatever you need.

Step 8: Re-Deploy Your App

This time when you deploy your app via git push, it will use the custom buildpack you set in Step 4. You should be able to view your app live on your custom port, subdomain, or domain now, and all should be well.

Now sit back, relax, and enjoy your own mini-Heroku at a fraction of the cost!