Symlink Deployment.


Posted by Trka in Systems on Aug 03, 2017

I kind of wrote this specifically for Jimmey, one of the not-so-fullstack-developers where I work. I do all my v2+ pushes using symlinks, and he's super-curious about symlink deploys because they go so smoothly, but when he googles all he can find is hacker stuff.

Info on symlink deploys should be far more accessible than that, because it's a really simple - albeit not exactly SOP - topic, and it's a huge entry-point to the world of CI\CD, and really devops in general.

So I wrote this - for Jimmey - so he would always know where to find a solid step-by-step for moving a production site to symlink deploy.'re welcome, Jimmey ;)

Symlink deployment is a strange word if you're not living in the linux world. If you are… you know what symlinks are, and you love them. For the Windows crowd: symlinks are… let's say they let one file/folder pretend to be another. Windows has links, and they let you jump to another place a lot like a web link works. Symlinks behave very differently. They have the content of the target, but the path is unchanged. It's a little more like a rewrite rule, I guess you'd say. Linux people use them a lot for installing apps. Say I have Awesome App v2 and it's AWESOME. version 3 comes out and it's AWFUL.
Without symlinks, that's unfortunate.
With symlinks, though:

  • I installed v2 as a symlink that looks like awesomeapp -> awesomeappv2
  • My executable is just awesomeapp
  • I installed v3 as awesomeapp -> awesomeappv3

The command didn't change, but the app did.

When it turns out v3 is awful, i type 4 words and the symlink points back to v2.
No problem.

Why this is relevant to what we're doing: deploying a new version of a live site can be scary. Maybe it hasn't been tested in its final environment, so you can only be so sure. Maybe there's downtime or cutover to worry about, so higher-ups want you to do it at midnight on Friday (and test all Saturday, with a tentative switchback on Sunday).

With symlinks: nope.

  • You get a grace period where both versions are live in the final hosting. Great for last-round checks.
  • The user facing cutover happens in 3-4 milliseconds, so downtime is a non-issue.
  • The older version doesn't get destroyed, so it's always there for legacy reference - unless you decide to remove it.

Sounds magical, right?!?! Unfortunately, if you're like most of the world that's hosting through whm on CentOS or RHEL, this isn't the default config. Why, is beyond me. Fortunately, it's harmless and simple to switch your VHost to symlink. The famous WHM+Apache+CentOS that you should already be familiar with has a directory structure that sends user traffic to the current site version along this path:
www -(symlink)-> public_html <files>
We'll shuffle that juuuust a little, so it follows a path more like
www -(symlink)-> public_html -(symlink)-> site_versions/v1 <files>
Then when we cutover, it's just a question of flipping the public_html symlink to point to site_versions/v2

As a bonus, we'll be co-hosting the two versions - without symlinks - directly from those directories. This way, we have (public), and we also have and for (respectively) prelaunch testing and postlaunch reference.

1. Convert to Symlinks

Fair warning: I've done this hundreds and hundreds of times, and no problems. But rm -rf public_html still feels so unnatural. It'll never feel right.

Sidenote: in ln -s source target, note the the source folder name doesn't have a trailing slash. This sort of hints at super-low-level "How filesystems work" - which isn't for this post. For now, suffice it to say: "Just don't use trailing slashes when creating\deleting symlinks".

su $acctname #puts us in /home/acctname with the right permissions
mkdir site_versions # make the new parent directory
cp -r public_html site_versions/v1 # copy the current site files the the v1 dir
rm -rf public_html # destroy public_html (the unnatural part)
ln -s site_versions/v1 public_html # make a symlink from v1 to public_html, restoring the chain

Now you're symlinked. Whoohoo! Your site's still good, but it's running from /home/$acctname/site_versions/v1

2. Make Subdomains

Setup a subdomain through the usual cpanel/whm route that points to site_versions/v1 and your legacy reference is done.

Setup another subdomain that points to site_versions/v2

3. Push the new code

Move your new site code to the site_versions/v2 directory. You've already setup a subdomain there, so you can testtesttest at

4. Cutover

rm -f public_html 
ln -s site_versions/v2 public_html

Done. You're live. Easy, right?
If it turns out tomorrow that there IS a critical bug in v2, it's just as easy to run those last commands to point public_html to site_versions/v1 while you work it out. Run

rm -rf public_html
ln -s site_versions/v1 public_html

And now your public site is v1, but you can work on right beside it.
No problem