Install video card on Lubuntu with NVIDIA chipset


Update: after upgrading to Lubuntu 16.10 the method below stopped working until I enabled the latest NVIDIA drivers using the GUI method.  After rebooting I was able to continue with the steps below.


I bought a new video card the other day from Zotac and I figured I would install the drivers via their website or the NVIDIA website (a downloadable file named  WRONG!

The best way to install NVIDIA drivers is through the official Ubuntu repositories:

sudo nvidia-installer --uninstall
sudo apt-get remove nvidia* --purge
sudo update-initramfs -u
sudo apt-get install nvidia-current

For most people, that should be sufficient -- reboot and verify.  If the resolution still looks off, read on...

It turns out I had black bars at the top and bottom of my screen and everything was fuzzy.  It turns out the NVIDIA auto-configuration didn't detect my old monitor (SyncMaster 920nw) and its weird 1440x900 aspect ratio so I needed to do some additional tweaking.

I ran this command to generate my /etc/X11/xorg.conf file (bypassing automatic monitor detection):

sudo nvidia-xconfig --no-use-edid-dpi

I then edited the /etc/X11/xorg.conf file and added the following line to the Monitor section:

Option "DPI" "96 x 96"

I also had to change the HorizSync and VertRefresh values to (check your monitor manufacturer page to find the right values for your monitor):

HorizSync       28.0 - 80.0
VertRefresh     48.0 - 75.0

Then, in the Screen section, I had to add:

Option "IgnoreEDID" "true"

After saving the file, I rebooted and loaded the NVIDIA config tool (Start > System Tools > NVIDIA X Server Settings).  In the X Server Display Configuration screen, I clicked the Resolution dropdown and chose 1440x900.  Once the resolution updated and looked good, I clicked the Save to X Configuration File button.

After rebooting to verify the change persisted, I had to update my /etc/default/grub file with these lines to improve my splash screen and grub list resolution:


(note: my BIOS video driver doesn't support 1440x900 so I just went with a decent alternative for my needs.  Feel free to run sudo hwinfo --framebuffer to check your options)

In addition, I needed to create/update the /etc/initramfs-tools/conf.d/splash file to add this line:


Finally, I ran these commands to apply the changes:

sudo update-initramfs -u
sudo update-grub2

I rebooted once more to verify the grub list and Lubuntu splash screen at startup looked moderately better.

...Hope this helps anyone else with an unusual setup like mine!



Real meaning of the season


For all my Christian friends...

The Brits have all the fun


Chocolate mixed with pop rocks candy and shaped like frogs... so cool!

UK only  :(

Tutorial: wildcard domain SSL/TLS certificates


Subject alternate domain (a.k.a. wildcard) TLS certificates are an efficient way to protect multiple domains without having to purchase multiple certificates.  Unfortunately, I had to dig quite a bit to figure out how to generate/update one using OpenSSL:

Generate a new key and cert:

Download myconfig.cnf and configure lines 127-150 and 224-230, install OpenSSL and then run:

openssl req -new -newkey rsa:4096 -keyout myprivate.key -nodes -extensions v3_req -config myconfig.cnf -out mycert.csr

Update cert (using an existing key):

openssl req -new -key myprivate.key -config myconfig_updated.cnf -out mycert_updated.csr

P.S. The wildcard domain is only single level (e.g. you can't do *.*

P.P.S. If you set up your config file with your desired defaults, you can avoid hitting Enter for each prompt by including -batch in your openssl commands above.

Finally... a decent security tool for Docker


For all of Docker's benefits and success, there has always remained an inconvenient truth: container security.  Like most set-it-and-forget-it solutions, many Docker users forget to implement an ongoing and regular security plan for keeping their containers patched.  The problem is that most Docker users simply download an "NGINX container" or "Ruby container" and they get their runtime or stack of choice but then don't consider that container comes with its own full-blown OS with many other components that need to be patched and upgraded frequently.  The result: many Docker containers are insecure.

That sad reality may be changing, though, with the introduction of CoreOS' Clair -- an open source security scanner and alerting tool that efficiently monitors your containers and notifies you when they need to be patched.  Sweet!

The reality of Git


Courtesy of xkcd

Woot! Quarter-million page views


The running joke on the webosphere is that for the vast majority of us, no one reads our blogs.  Well, this week marks the quarter-million page view milestone for my humble blog.  Sweet!

Thank you, anonymous readers and commenters, for improving my self-esteem  :)

January 2017 update: I'm now at a half-million.  Double-woot!

Atom shows promise


Update: My efforts have switched to Brackets, another open source text editor that is much faster.  I recommend this font fix, the Tomorrow Night theme, and the following extensions:

  • Autobabel
  • Brackets Beautify
  • Brackets Snippets (by edc)
  • Brackets TSLint
  • Brackets TypeScript
  • brackets-eslint
  • Brackets-Gulp
  • CloseAllOpenFiles
  • CSSLint
  • Open project in terminal
  • Proper Indent
  • Tabs - Custom Working
Also, if you want to assign a custom extension (like AWS' CloudFormation *.template) to a syntax highlighting language (like JSON), use this method.


This article assumes you don't hate JavaScript.  Let's face it, JavaScript in some form or fashion is here to stay.

If you want to be a JavaScript/HTML5/Hybrid/Node developer, you'll need to pick an editor/IDE.  Although heavyweight options exist (Eclipse, NetBeans, Visual Studio, etc), most agile developers prefer a more nimble tool, like Sublime Text, Komodo IDE, or WebStorm.  A relative newcomer to the crowd is Atom from GitHub (the company).

Unlike many tools in this genre, Atom is free.  In addition, it is cross-platform and there are lots of plugin packages and themes that allow you to extensively customize it.  That said, at times it can be woefully slow, even on a powerful 8 core/8 GB memory laptop:

That said, the brilliant engineers at GitHub and the passionate Atom community continue to make improvements and I anticipate the experience will only improve with time.

If you're not ready to shell out hard cash for another tool or you'd prefer to support Open Source, here are a few steps that worked well for me:

1. Install Git

2. Install Node 4 (this incorporates many improvements from the io.js fork)

3. Install Atom

At this point, you could theoretically create a Hello World! app, but in reality you're going to need a ton more tools and libraries.  That's because the good ol' days of creating websites in Notepad are long gone.

More surprisingly, the good recent days of creating websites in JavaScript and CSS are gone.  That's because most modern development shops use transpilers to create cleaner, more robust code, whether it's transforming LESS or SASS into CSS, ES6 or TypeScript into JavaScript, etc.

This reality has some drawbacks as a barrier to entry for new or casual developers:

There's another hidden downside: performance.  Even cutting-edge frameworks and toolsets suffer from the inherent modularity of Node:

Another sad truth: Windows stinks when it comes to development.

node-gyp alone adds an appalling 8+ GB to your setup process.  The idea that I have to install Visual Studio Express and an old version of Python just to have a compiler for Node to work is ridiculous.  And the bad news just gets worse.

There are many, many hacks you could apply to bandage over this reality but it will eventually dawn on you that you need an OS from a Unix/Linux pedigree to be a truly effective developer.

Okay, enough of the rant tangent.  Let's get back to Atom!

Assuming you're still reading this lengthy post (congrats!), here are a few configuration tips:

1. First, you'll want to install some useful global Node packages.  Open a terminal/command prompt and enter:

npm install -g babel babel-core babel-eslint babelify eslint gulp gulp-less jspm less napa node-gyp yo

2. Now, you'll want to install some useful Atom packages:

apm install atom-terminal atom-typescript cssfmt gulp-watcher es-identifier-highlight jsfmt language-babel linter linter-eslint linter-stylelint linter-tslint project-jump project-view turbo-javascript

3. You can also optionally install an Atom theme.  I personally like Outlander:

apm install outlander-syntax outlander-ui file-type-icons

4. In addition, choose and install a monospace font that fits your style.  I like DejaVu Sans Mono.

5. Now, open Atom, go to File > Settings and set the desired font family and increase the font size to 15 (your eyes will thank me a decade from now)

P.S. While you're in this screen, feel free to set your Project Home to match your GitHub Desktop projects location.

6. Next, click on Themes in the left-column menu and set your desired theme:

7. Finally, you'll want to set a keyboard shortcut for opening projects.

Note: If you're having issues with creating a custom ESLint ruleset, check out


There you have it!  A powerful, free code editor that looks and acts like all the other cool kids' screens at tech conferences  :)

DevOps defined


Rob England has a nice overview of various DevOps definitions and I like the one he settles on:

DevOps is agile IT delivery; the philosophy of unifying Development and Operations at the culture, practice, and tool levels, to achieve accelerated and more frequent deployment of changes to Production.

Docker simplified


Update: Tutm is now part of Docker

I've been tracking a number of Docker providers and services for quite some time now and I've been increasingly impressed with the constantly improving Tutum service.  Here's a great video that talks about all the pieces you would need to build/support if trying to do it yourself and why the ROI becomes obvious for you to pay the experts so you can focus on your core business:

P.S. If you'd like to stay up to speed on the latest Docker news, developments, and tips, they also have a useful curated newsletter

P.P.S. I'm not affiliated with Tutum or any of their partners.

Aurelia: the next revolution


Update: The Aurelia team missed their beta release goal, but these things often happen in the open source world.  I guess we just need to be patient and hope it comes soon.

Update #2: Beta 1 has landed!


I'm almost always wrong with technology predictions (I'm looking at you, SVG) so don't put too much stock in this, but I think Aurelia is going to be the primary contender against Angular 2 and React/Flux.

With the financial backing of Durandal Inc.TypeScript support, a bundling system, and their pseudo-secret Project X (a.k.a. Aurelia Interfacea mobile framework like Ionic), Rob Eisenberg & Team are really working hard to deliver a polished beta in four weeks!

Dumb logo


Am I the only one that thinks Google's new logo looks stupid?  It's like a dumbed-down crayon exercise for kindergartners.  Now, I'm not a typographical elitist that spends weeks debating the nuances of Helvetica, but for crying out loud why would you abandon an iconic and visually appealing "g" for that atrocity?  ...and don't even get me started on the "e" -- it reminds me of Internet Explorer, ughh!

Move over Java...


PHP is the new market leader:

Source: Jelastic

Lessons learned from an Acquia migration


Acquia is a popular Drupal hosting service, but I was surprised at how many "gotchas" I encountered when migrating a number of sites to their environment.  I've listed them below (in no particular order) in case others find it useful.  Note: some of these are general Drupal issues rather than Acquia specific.

If you run into a cache issue (i.e. data is correct in the appropriate database table but doesn't show correctly in the UI and clearing caches via the Drupal admin UI doesn't work) you may need to clear memcache

If you're using drush sql-query in your post-db-copy Acquia Cloud Hook, you need to include the --database parameter for a multisite:



drush @$site.$target_env sql-query "select * from node;" --database=$db_name

Make sure to disable modules before deleting them (especially Boost, since Acquia Purge will complain).  If, for whatever reason, that option isn't available, you can manually remove the database entry: delete from system where name = 'boost';

The default Developer role doesn't work with Acquia Dev Desktop 2.  You need to provide at least Senior Developer access.

It's not obvious how to change the Acquia Cloud list label:
  1. Login to Acquia Cloud
  2. Go to the Subscriptions tab
  3. Select the Subscription you would like to edit
  4. Click Edit
  5. Change the name

BlazeMeter is restricted to 40 tests and a 30-day trial.  Unlike other add-ons (which are not trials and don't have expiration dates), the BlazeMeter limited trial begins when you activate the add-on so be careful to activate it only when you are about to go live and need to run the required load test.  Also, if you have more than 40 sites, you can combine sites in a single test by including multiple site URLs.

When using Acquia Dev Desktop 2, previous installations of an older version of Drush may cause problems.  On Windows, check your PATH environment variable and on OS X check your ~/.bash_profile file for non-DevDesktop references to Drush and remove them.


Acquia Search issues: 1, 2, 3

In Acquia Dev Desktop 2, the common git error "Cannot push missing reference (4:-11)" is vague and unhelpful.  It basically means you need to do a git pull before a git push.

Stage File Proxy does not support subfolders within your Drupal user files directory. Any images or other files within subfolders will not be displayed correctly in the RA environment.  Update: This is possibly fixed in latest dev

New Relic is incompatible with PHP 5.6.  Use PHP 5.5 instead.

Multisites require additional New Relic configuration.  The sample settings.php Acquia snippet didn't work for me so I ended up using:

if (extension_loaded('newrelic')) {
  $exploded_path = explode('/', dirname(__FILE__));
  $site_domain = array_pop($exploded_path);
  newrelic_set_appname($_ENV['AH_SITE_ENVIRONMENT'] .'_'. $site_domain, '', 'true');

Note: "Custom app names defining individual sites are initialized later in the Drupal bootstrap process, so New Relic can not report on Drupal modules, views, and hooks on a per-domain basis when this method is used. This data is only available for the environment as a whole"

Make sure to use the Acquia Dev Desktop 2 version 27 July 2015 or later to fix a Drush/OpenSSL bug.

If using the Acquia Drupal distribution profile, the status report may indicate there's a Drupal core update available when in fact you are running the latest core.  This is a known issue and "As a result, we often recommend moving away from the profile."

You may encounter periodic fatal errors (white screens) as a result of updating modules like CTools or Rules (for example, Fatal error: Class 'RulesAbstractPlugin' not found in /.../docroot/sites/all/modules/contrib/rules/includes/ on line 11).  You need to download and run registry_rebuild to fix it.

Acquia cron commands are limited to 255 chars.  If you have a longer command, put it in a shell script and reference that instead.

Cloud hook scripts created on Windows machines don't have their executable bit set.  Here's the fix.

Cloud hook will revert all features on code deploy.  To keep an override (such as disable CSS/JS aggregation), use the "Lock" mechanism as described in the "Living with Overrides" section here.

Always include a version (e.g. "version = 7.x-1.0") with your Feature (otherwise the module_filter display is messed up).

The Acquia documentation for incompatible modules is incomplete.  Use this version instead.

Acquia Dev Desktop 2 uses a .gitignore default that prevents settings.php from being sent.  Create a sites/.gitignore file and add this line: !*/settings*.php

If you add a new site to an existing multisite Acquia Dev Desktop 2, you need to do an immediate code pull from dev after pushing to dev the very first time so your local settings.php file will be updated with the Acquia dev database credentials.

If memcache does not appear to be running or working, run /update.php

In Acquia purge, "the standard domain detection often can detect too many domains and may cause cross-site purging".  You need to identify the Acquia purge domains because otherwise it purges pages for all domains in a docroot!  Add this to your settings.php file:

if (isset($_SERVER['HTTP_HOST']) && (!empty($_SERVER['HTTP_HOST']))) {
  $conf['acquia_purge_domains'] = array($_SERVER['HTTP_HOST']);

The self-signed cert in Acquia Dev Desktop 2 still throws a Chrome intercept warning so it's probably easiest just to disable HTTPS when working locally.

It's not currently possible for a single environment to have multiple elastic load balancers (ELBs) so you'll need to unify all your multisite sites into a single cert: 1, 2

Features sometimes adds a "project = " line in the info file which causes the Drupal update check to look for it on  Remove that line if it exists.

img style is stripped when using the "Limit allowed HTML tags" in /admin/config/content/formats/filtered_html  See 1, 2, 3


From Acquia tech support: "You cannot (or rather should not) point an A record (e.g. a bare domain like at an ELB's IPs because they are ephemeral. The bare domain has to point with an A record to the IP of the active balancer."

The core overlay module affects performance and can be a security risk.  Disable it.

rsync has a ~260 char filename limit (including path!) on Windows which can break Acquia Dev Desktop 2: 1, 2

Acquia Dev Desktop 2 local shortcuts and symbolic links should be excluded from git.  Create/edit a sites/.gitignore file and add these lines:


Private files require additional configuration.

You can use the @sites alias, but it needs to come after the Acquia docroot alias.  For example:  drush @sites cc all

Ad-blocking add-ons can affect page loading so check incognito mode before filing a help ticket.

The server time was 7 hours off of our local time so when we created a cron job to run at 10pm we were surprised to discover it was actually running at 3pm local time (during peak site traffic).  So, remember to run date on the server to check the time and adjust your cron jobs accordingly.

If you need SSL/TLS for your apex domain (aka "bare" or "naked" domain), such as, you'll need to purchase a managed DNS service that supports ALIAS or ANAME records.  Acquia has a great (albeit buried) guide for Amazon's Route 53 service.

If you use SSL/TLS for your site and you use the acquia_purge module, you need to add a rather buried setting to your settings.php file.

If you use webform and a private file system path is configured, you need to add an additional settings.php setting to avoid zero-byte webform result downloads

If you use the Remote Administration (RA) service and Acquia Cloud Hooks you'll likely run into conflicts.  Disable your hooks in the RA environment with:

if [ "$AH_SITE_ENVIRONMENT" != "ra" ]; then
# your code here...

VAAMP - quickstart


This is a quickstart guide for getting up and running with VAAMP (VirtualBox, Alpine Linux, Apache, MySQL, and PHP).  For more details, see the full tutorial.

One-time Setup

1. Download and install VirtualBox

2. Download and extract the VAAMP zip file

3. Open VirtualBox and choose File > Import appliance... and browse to the AlpineLinux.ovf file from Step 2

4. Once the import completes, click the VirtualBox Start button

5. Edit your hosts file and add the following to the bottom: adminer.local.dd logs.local.dd

6. Browse to http://adminer.local.dd/ and verify the Adminer MySQL application displays.

7. Browse to http://logs.local.dd/ and verify the Pimp my Log application displays.

8. Create a file called vaampkey.txt and copy the contents of into it.

9. Install an sshfs client:

OS X: Install osxfuse and sshfs by following this guide
Windows: Download and install (note: the installer may warn you about missing components but they will be automatically downloaded and installed as part of the sshfs installation process).  Also, the install may require a computer restart. 
Red Hat: sudo yum install fuse-sshfs 
Ubuntu: sudo apt-get install sshfs

10. Open sshfs and click the + Add button

11. Now, enter the following information:
  • Drive Name = vaamp
  • Host =
  • Port = 22
  • Username = www
  • Authentication method = PrivateKey
  • PrivateKey = YOUR/PATH/TO/vaampkey.txt
  • Directory = /usr/local/sites
  • Drive Letter = V
12. Then click Save and then Mount (note: you MUST click Save first due to bug #85)

Add a new site

In this example, I'll install a Wordpress site.

A.  Edit your hosts file and add the following to the bottom: mywordpress.local.dd

B. Open your V: drive and extract the latest Wordpress version into it.

C. Rename the wordpress folder to mywordpress.local.dd

D. Browse to http://adminer.local.dd/ and create a new database and user.

E. Browse to http://mywordpress.local.dd/ and follow the setup wizard

VAAMP - part 3


In this three-part series, I'll walk you through setting up a cross-platform LAMP server for local development.  I'll be using VirtualBox, Alpine Linux, Apache, MySQL, and PHP so I've nicknamed it VAAMP.


VirtualHost, shared folder, and DNS setup



If you don't want to manually perform all the steps below, you can just download the VirtualBox appliance here.


At this point we have a basic LAMP server running in VirtualBox.  Unfortunately, it's not especially useful for local development.  Creating and editing files via a command line interface is clunky and we're currently limited to running only a single local website.  Let's fix all that!

1.  Make sure to follow Part 1 and Part 2 of this guide and login as root

2.  Install a few needed packages by running this command:

apk add git sudo mysql-client php-phar php-openssl

3.  Next, install the Composer PHP dependency manager by running these commands:

curl -sS | php

mv composer.phar /usr/local/bin/composer

4.  Now we need to configure Apache to support multiple websites via VirtualHosts.  We could create separate config entries for each website but that gets rather tiresome so a more elegant solution is to use a single wildcard VirtualHost to support unlimited websites.  Also, we'll want to tweak the Apache configuration to support .htaccess files and run under a non-root account.

mkdir /usr/local/sites

adduser -D www www && echo "www:web-master" | chpasswd

sed -i '/^ProxyPassMatch\W/d' /etc/apache2/httpd.conf
sed -i '/^DirectoryIndex\W/d' /etc/apache2/httpd.conf
sed -i 's/AllowOverride\sNone/AllowOverride All/Ig' /etc/apache2/httpd.conf
sed -i 's/^User\sapache/User www/' /etc/apache2/httpd.conf
sed -i 's/^Group\sapache/Group www/' /etc/apache2/httpd.conf
sed -i 's/^user\s=\snobody/user = www/' /etc/php/php-fpm.conf
sed -i 's/^group\s=\snobody/group = www/' /etc/php/php-fpm.conf

cat <<EOT>> /etc/apache2/httpd.conf
  ServerName local.dd
  ServerAlias *.local.dd
  UseCanonicalName Off
  VirtualDocumentRoot "/usr/local/sites/%0"
  RewriteEngine On
  RewriteRule ^/(.*\.php(/.*)?)$ fcgi://{SERVER_NAME}/\$1 [P]
  DirectoryIndex index.htm index.html index.php
  ErrorLog /var/www/logs/error.log
  CustomLog /var/www/logs/access.log vhost_combined

5. Basically, the commands above set Apache/PHP to run as the www user and will redirect any *.local.dd http request to a corresponding *.local.dd folder in /usr/local/sites  Let's go ahead and test it out by moving adminer into its own site

mkdir /usr/local/sites/adminer.local.dd && mv /var/www/localhost/htdocs/adminer.php /usr/local/sites/adminer.local.dd/index.php

6. Then we set the proper permissions and restart Apache/PHP

chown -R www:www /usr/local/sites

/etc/init.d/apache2 restart
/etc/init.d/php-fpm restart

7.  Before we can load http://adminer.local.dd/ in our browser, though, we need to tell our computer what it is since that domain isn't a valid Internet address.  Follow these instructions to add this line to the bottom of your hosts file: adminer.local.dd

8.  Okay, let's try it out!  Open a browser window and go to http://adminer.local.dd/   You should see the Adminer login screen:

9.  You're probably thinking "Umm...we loaded Adminer in Part 2 of this series.  What's the big deal?"  Well, in addition to this Adminer website, we can now run additional separate websites in our virtual machine.  As an example, let's install Drupal.  As a prerequisite we'll need a database so let's set one up now.  In the Adminer screen, type root for the Username and then click Login

10. Next, click Create new database

11. Type mydrupalsite for the name and choose utf8_general_ci for the collation type:

12. Click the Save button and click the Privileges link

13. Click the Create user link, provide a username and password, click the All privileges checkbox, and then click Save

14. Now that we have a database for our Drupal site, let's download Drupal and set it up.  Since Apache/PHP runs as the www user, we need to switch accounts first:

sudo su - www

15. There are a couple ways to install Drupal, but I prefer using a Drupal automation tool called Drush.  Let's install the latest version:

composer global require drush/drush:dev-master

16. Now that Drush is installed, let's add it to our PATH so we can reference it anywhere on the command line:


ln -s /home/www/.composer/vendor/drush/drush/drush /usr/local/bin/drush

sudo su - www

drush --version

16. Although we can use Drush to perform the entire Drupal install, I'm just going to use it to download Drupal and then walk through the rest of the steps manually so you can follow along:

cd /usr/local/sites/ && drush dl drupal && mv drupal-* mydrupalsite.local.dd

17. Use the instructions in Step 7 to add the following to your computer's hosts file: mydrupalsite.local.dd

18. Load http://mydrupalsite.local.dd/ in your browser and follow the Drupal installation wizard (when prompted for database credentials, use the database name/username/password from steps 10-13 above)

19. You should now have Drupal running inside your virtual machine.  Woot!

20. Your next question is probably "Great, but how do I access the files now?"  Well, normally you would set up a VirtualBox shared folder to synchronize the websites directory with a folder on your local machine but unfortunately Alpine Linux doesn't support the VirtualBox guest additions yet so we need another approach.  Although you could do something like FTP, Syncthing, etc., the most elegant solution I've found is sshfs.

Note: since this virtual machine isn't accessible from anything but the host, we'll forsake some security in lieu of convenience by using a pre-generated ssh keypair.

If you feel more comfortable, you can delete the /home/www/.ssh folder and setup your own keypair.  
21. Switch back to the Alpine Linux virtual machine command line, make sure you're in the www account (by typing whoami), add then add the pre-generated public key:

mkdir ~/.ssh && chmod 700 ~/.ssh && cd ~/.ssh && curl -s > authorized_keys && chmod 600 authorized_keys

22. On your host machine, create a file called vaampkey.txt and copy the contents of into it.

23. Now you need to install the sshfs client:

OS X: Install osxfuse and sshfs by following this guide
Windows: Download and install (note: the installer may warn you about missing components but they will be automatically downloaded and installed as part of the sshfs installation process).  Also, the install may require a computer restart. 
Red Hat: sudo yum install fuse-sshfs 
Ubuntu: sudo apt-get install sshfs

24. Open sshfs and click the + Add button

25. Now, enter the following information:
  • Drive Name = vaamp
  • Host =
  • Port = 22
  • Username = www
  • Authentication method = PrivateKey
  • PrivateKey = YOUR/PATH/TO/vaampkey.txt
  • Directory = /usr/local/sites
  • Drive Letter = V
26. Then click Save and then Mount (note: you MUST click Save first due to bug #85)

27. You can now add/update/delete website files via your V: drive!

28. Last but not least, we need an easy way to check the server logs.  I like Pimp my Log so let's use that.

mkdir /usr/local/sites/logs.local.dd

cd /usr/local/sites/logs.local.dd

git clone .

29.  Now we need to turn on PHP logging by exiting the www user account and logging into the root account and then running:

sed -i 's/^;error_log\s=\sphp_errors\.log/error_log = /var/log/apache2/php_errors.log/' /etc/php/php.ini

/etc/init.d/php-fpm restart

touch /var/log/apache2/php_errors.log

30. Finally, we need to add the following line to our computer's hosts file logs.local.dd

31. When you load http://logs.local.dd/ in your browser you should see a welcome page:

32. Click Configure Now and then click No when asked if you want to protect the log website with an admin account (note: normally this is an important security measure, but since no one can access this virtual machine except our host, we forego the extra overhead)

33. The wizard should detect your Apache and PHP setup.  Click Continue

34. The wizard should detect your Apache logs.  Click Continue

35. The wizard should detect your PHP logs.  Click Continue

36. That's it!  Click Pimp My Logs Now to see your logs.

If you've made it this far, congratulations!  I hope you enjoyed the experience and learned a few things along the way that may help you discover and configure your preferred local development experience.

P.S. Check out the quickstart guide for a handy reference.



Ready for the next version of HTTP?


image credit: O'Reilly

"You will not need to change your websites or applications to ensure they continue to work properly. Not only will your application code and HTTP APIs continue to work uninterrupted, but your application will also likely perform better and consume fewer resources on both client and server."
Source: Akamai

"Although the standard itself does not require usage of encryption, most client implementations have stated that they will only support HTTP/2 over TLS [1.2+], which makes encryption de facto mandatory."

Source: Wikipedia

Resolving git subtree confusion


FYI, there used to be two different git features called subtree which caused a lot of confusion: the official git subtree merging strategy and a third-party open source git subtree script.

As of git version 1.7.11 (July 2012), the script has been merged into the official mainline git (as an optional contrib).

There are also the stree and subrepo projects that are worth considering.

Zach King


Cool video (vine) magician:

VAAMP - part 2


In this three-part series, I'll walk you through setting up a cross-platform LAMP server for local development.  I'll be using VirtualBox, Alpine Linux, Apache, MySQL, and PHP so I've nicknamed it VAAMP.


Apache, MySQL, and PHP setup



If you don't want to manually perform all the steps below, you can just download the VirtualBox appliance here.


1.  Make sure to follow Part 1 of this guide and login as root

2.  Install Apache httpd, MySQL (MariaDB), and PHP-FPM by running this command:

apk add gd mysql curl apache2-proxy php-fpm php-common php-iconv php-json php-gd php-curl php-xml php-mysql php-mysqli php-pdo php-pdo_mysql php-imap php-soap php-xmlrpc php-posix php-mcrypt php-gettext php-ldap php-ctype php-dom php-pear

3.  Configure Apache with these commands:

sed -i 's,\(LoadModule mpm_prefork_module modules/\),#\1,g' /etc/apache2/httpd.conf

sed -i 's,#\(LoadModule mpm_event_module modules/\),\1,g' /etc/apache2/httpd.conf

4.  Set httpd-mpm to default values:

cp /etc/apache2/original/extra/httpd-mpm.conf /etc/apache2/conf.d/

5.  Assuming you kept the hostname as localhost in Part 1, Step 24, configure PHP via these commands:

sed -i 's,\(DirectoryIndex index.html index.html.var\),#\1,g' /etc/apache2/httpd.conf

echo 'ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://$1' >> /etc/apache2/httpd.conf

echo 'DirectoryIndex index.html index.php' >> /etc/apache2/httpd.conf

echo '<?php phpinfo(); ?>' > /var/www/localhost/htdocs/phpinfo.php

6.  Initialize MariaDB:

/etc/init.d/mariadb setup

curl -sLo /var/www/localhost/htdocs/adminer.php

7.  Add Apache, MariaDB and PHP to the OpenRC init process (so they start at boot) and then start them:

rc-update add apache2 default && rc-update add mariadb default && rc-update add php-fpm default

/etc/init.d/apache2 restart && /etc/init.d/mariadb restart && /etc/init.d/php-fpm restart

8.  To test Apache, assuming you used in Part 1, Step 24, open a browser on your host computer and go to

9.  To test PHP, go to

10. To test MariaDB, go to and click Login

Congratulations!  You've installed a LAMP server on VirtualBox.  Head on over to the next post in this series: VirtualHost, shared folder, and DNS setup



The Man gains a point


Sadness personified into a webpage:

The perfect local development domain


Many website developers work locally on their laptops or personal computers before posting code to a centralized web server.

When running a website locally, you use (or the common alias http://localhost/ )

However, these URLs are not especially elegant or descriptive.  So, developers typically edit their hosts file to provide a more descriptive domain, like or

There are some dangers with this approach so we'll discuss the common approaches and suggest the best option:


The problem with creating your own made-up top-level-domain (TLD) is that you run the risk of that imaginary TLD becoming an actual TLD.  For example, let's say you run a company called ACME and a decade ago your IT team standardized on using *.website for all your local websites.  So, would load your public Internet site and would load your private internal company intranet site (for all machines on the network whose hosts file was configured appropriately).  This worked great...until 2014 when *.website became a generic top-level-domain (gTLD).  What if the person that reserved was a hacker or competitor?  If a new employee didn't have the hosts file configured yet (or someone was using their mobile device, etc.), they would inadvertently load a different site controlled by that external entity.  Bad news!  You're probably thinking, "Well, I'll just pick a domain suffix that no one will ever really think to register".  The reality is, with the proliferation of new gTLDs, it's just not a safe bet to create your own.


You may be thinking, "Isn't there any domain I could use for local offline sites?!"  Well, there are -- they're called reserved domains and they were built for that very purpose: example, invalid, localhost, test, and local.  None of these are particularly attractive except for the obvious choice: *.local.  Problem solved, right?  Not so fast.  Unfortunately a particular company (*cough*Apple*cough*) decided to abuse that domain for it's own purpose so they ruined it for the rest of us and it's no longer recommended for use.  Note: technically, *.test might be an acceptable alternative, but most companies already have a dedicated test environment so using *.test for a local site would be confusing.

Purchase your own dedicated-purpose domain

The safest way to ensure a particular domain suffix won't become official or be used by others is to simply buy it.  The downside to this approach is that it costs money (unless you use a free service [1, 2, 3]) and there's a slight risk of someone forgetting to renew the domain at some point and a squatter claims it, thus introducing the potential issue described in *.dev above.


This country code top-level-domain (ccTLD) was originally reserved for for East Germany but never implemented (due to the reunification of Germany).  Importantly, it was never added to the root nameservers so browsers won't recognize it as an Internet address.  Equally important, it was used internally by a couple universities so that historic precedence means *.dd won't be revoked and reassigned to a different country in the future.  It's also easy to type and nondescript -- perfect for local development.


My recommendation is to use *.local.dd because it's more descriptive and allows more flexibility -- for example, you can't whitelist *.dd in Ghostery but you can whitelist local.dd (which then whitelists all its subdomains).

VAAMP - part 1


In this three-part series, I'll walk you through setting up a cross-platform LAMP server for local development.  I'll be using VirtualBox, Alpine Linux, Apache, MySQL, and PHP so I've nicknamed it VAAMP.


Your first question is probably "Why not just use Docker, Vagrant, or XAMPP?"

Docker shows good promise, but is wildly in flux so I'll wait until OCP OCI becomes standardized.  It's also somewhat complicated to set up and use for a novice programmer simply looking to develop locally.

Vagrant does a good job of simplifying the setup and run process but customizing it requires knowledge of Ruby (which many developers may not know) and Windows support in version 1.7.2 is buggy.

XAMPP avoids additional virtualization overhead by installing Apache and PHP directly on the host OS but isn't easily scriptable to create a custom development workflow suited to your unique tastes and needs (such as installing Drush along with the LAMP stack).

Since VirtualBox is free, supports Windows, Mac, and Linux, and provides a command-line interface, it's a nice fit for my needs.  Also, Alpine Linux is smaller and more secure than many other common Linux distros so it's a good fit for basic local development.  Caveat: the Alpine Linux VirtualBox guest additions are currently in testing (at the time of this writing) so you're limited to a VirtualBox screen size of 800x600 (fortunately our use case leverages the host browser and a shared file system to communicate with the VM so this shouldn't be a problem).

VirtualBox and Alpine Linux setup



If you don't want to manually perform all the steps below, you can just download the VirtualBox appliance here.


1.  Download the latest alpine-mini1 iso file

2.  Download and install VirtualBox

3.  Open VirtualBox and choose File > Preferences...

4.  In the Network section, click on the Host-only Networks tab and then click on the + button.  This will create a new network interface

5.  Once the new network interface has been created, make sure it's selected and then click on the screwdriver button

6.  Set the IPv4 Address to and the IPv4 Network Mask to

7.  Then, click on the DHCP Server tab and uncheck the Enable Server checkbox (we'll be using a static IP instead of DHCP for this network interface)

8.  Click OK multiple times until you return to the default VirtualBox window.

9.  Click New and provide a descriptive Name and for Type choose Linux and Version choose Other Linux (64-bit)

10.  Assign the desired amount of memory to the VM

11. Create a virtual hard drive

12. Choose the VMDK format (since it's more widely used by other virtualization platforms and tools)

13. Use a dynamically allocated virtual hard drive

14. Indicate the desired disk size and then click Create

15. Once the VM has been initialized, make sure it's highlighted in the list and then click Settings

16. Click on Storage and then click on the Empty CD-ROM and use the dropdown button to choose the alpine-mini iso file from step 1 above

17. Then, click on Network and verify Adapter 1 is set to NAT

18. Click on the Adapter 2 tab and click the checkbox to enable it and choose Host-only Adapter and choose the new network interface you created in step 5

19. Click OK multiple times until you return to the default VirtualBox window.

20. Click Start

21. Login as root

22. Then run setup-alpine

23. You'll be prompted to select your keyboard

24. Choose a hostname (or just press ENTER to keep the default localhost).  For eth0 (NAT) press ENTER to keep the default dhcp.  For eth1 (host-only) type and press ENTER.  Then, press ENTER for the remaining network options to keep the defaults.  Note: in the remainder of this guide, if I don't provide a recommendation, you can just use ENTER to keep the default Alpine Linux recommendation.

25.  Choose a mirror (I chose 1 since the default scan 'f' seemed to hang)

26. When prompted to choose a hard disk, select sda and then sys and finally type y to install Alpine Linux to the virtual hard disk you created in step 14.

27. After the installation is complete, type poweroff to shut down the VM

28. Click on Settings

29. Click on Storage and then click on alpine-mini to highlight it.  Click on the CD-ROM dropdown and choose Remove disk from virtual drive and click OK

30. The CD-ROM drive should now show as Empty

31. Click OK multiple times until you return to the default VirtualBox window.

32. Click Start

33. When the VM boots, login as root with the new password you were prompted to set during the alpine-setup phase.

34. You should be able to access the Internet (thanks to the eth0 NAT network interface)

35. You should also be able to ping the Alpine Linux guest VM from your host computer (thanks to the eth1 host-only network interface)

Congratulations!  You've installed Alpine Linux on VirtualBox.  Check out the next post in this series: Apache, MySQL, and PHP setup