Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

When things go wrong: Rollback

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $9.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Yea... things go wrong. But, but, but! Ansistrano is cool because if something fails, the symlink will never change and the site will continue using the old code. If you're following our "Safe Migrations" philosophy, then deploying is even safer: if your migrations run, but the deploy never finishes, your non-destructive migrations won't hurt the current code.

But sometimes... a deploy will finish... only for you to have the sudden, horrible realization, that something is now massively wrong with the site. Like, there are zombies all over it or something.

At this moment, fate presents you with 3 options:

  1. Run for your life!
  2. Quickly make a new commit to fix things and re-deploy.
  3. Rollback to the previous deploy.

Other than running out of the building screaming, rolling back is the fastest way to escape the problem. And fortunately, Ansistrano has a second role all about... rolling back: ansistrano.rollback.

To install it, open the requirements.yml file and add an entry. For the version... let's see. The latest version right now is 2.0.1. Let's use that:

... lines 1 - 6
- src: ansistrano.rollback
version: 2.0.1

Tip

Due to the changes in Ansible Galaxy, Ansistrano is installed now via ansistrano.rollback instead of the old carlosbuenosvinos.ansistrano-rollback.

To install the role, on your local terminal, run:

ansible-galaxy install -r ansible/requirements.yml

Creating the Rollback Playbook

The rollback process will be its own, simple playbook. Create it: rollback.yml. I'll open deploy.yml so we can steal things... starting with the host. Then, of course, we need to include the new role: ansistrano.rollback:

---
- hosts: aws
roles:
- ansistrano.rollback

Rolling back is way simpler than deploying, but it works in the same way: there are a few stages and we override variables to control things. The only variable we need to override is ansistrano_deploy_to. In deploy.yml, we imported a vars_files called vars.yml, and used it to help set this.

Let's do basically the same thing here. Copy part of the vars_files section, paste it, and just import vars.yml: we don't need the vault:

---
- hosts: aws
vars_files:
- ./vars/vars.yml
... lines 6 - 13

Back in deploy.yml, also steal ansistrano_deploy_to and add that to rollback.yml:

---
- hosts: aws
vars_files:
- ./vars/vars.yml
vars:
# Ansistrano vars
ansistrano_deploy_to: "{{ project_deploy_dir }}" # Base path to deploy to.
... lines 10 - 13

Rollback!

And... yea... that's basically it! So... let's try it! On the server, I'm already in /var/www/project. My current symlink is set, and releases has 3 directories inside.

Back on your local terminal... rollback!

ansible-playbook ansible/rollback.yml -i ansible/hosts.ini

That's it. It should only take a few seconds. It does delete the old release directory, but this - like most things - can be controlled with a variable.

Done! Back on the server... yes! The symlink changed. And one of our releases is gone!

Running Down Database Migrations

So rolling back is pretty easy. The most common issue involves migrations. Again, if you follow our "safe migrations" philosophy, you have nothing to worry about. But, if you're a bit more reckless - hey, no judgment - then you may need to manually run the down direction on some of your migrations after a rollback.

Tip

The variable is now called ansistrano_rollback_before_symlink_tasks_file instead of ansistrano_before_symlink_tasks_file.

Let's add a little "opportunity" for us to do that. Let me show you: copy the ansistrano_before_symlink_tasks_file variable. In rollback.yml, paste this and set it to a new rollback/before-symlink.yml:

---
- hosts: aws
... lines 3 - 6
vars:
... lines 8 - 10
# Hooks: custom tasks if you need them
ansistrano_before_symlink_tasks_file: '{{ playbook_dir }}/rollback/before-symlink.yml'
... lines 13 - 16

Now, create a new rollback/ directory with that file inside. Here, we'll add just one task: "Pause to run migrations manually down". Use the pause module to freeze the playbook and put up a message:

---
- name: Pause to run migrations manually down
pause:
prompt: "Please, run 'bin/console doctrine:migrations:execute YYYYMMDDHHMMSS --down' manually in '{{ ansistrano_release_path.stdout }}' if needed and press enter to continue"

This is our opportunity to manually execute any down migrations we want.

Hey, I know: it's not automated and it's not awesome. But, things have gone wrong, so it's time for us to take over.

Let's rollback one more time:

ansible-playbook ansible/rollback.yml -i ansible/hosts.ini

Here's the pause: it shows us the directory we should go into. Hit enter and it keeps going. Oh, and cool! It failed on one of the servers! That was unplanned... but awesome! That's the new server, and apparently we've only deployed there two times. So, there was no old version to rollback to. Ansistrano smartly prevented the rollback... instead of rolling back to nothing.

Ok guys... we're done! Woh! This tutorial was a crazy ride - I loved it! And I hope you did too. You can now deploy a killer Symfony site - or any site with Ansistrano. If this was all interesting but felt like a lot of work, don't forget about the platform-as-a-service options like Heroku or Platform.sh. You don't have quite as much flexibility, and they're sometimes a bit more expensive, but a lot of what we learned is handled for you and you can get started really quickly.

Whatever you choose, go forth and build something awesome! Ok guys, I'll seeya next time!

Leave a comment!

6
Login or Register to join the conversation
Jt-B Avatar

Hi,
I just stumbled upon an updated var name for rollback:
ansistrano_rollback_before_symlink_tasks_file instead of the one mentioned in the script ( ansistrano_before_symlink_tasks_file).

Thanks for all.
Cheers
J

1 Reply

Hey Jt-B!

Thanks for mentioning it!

Cheers!

Reply
Cesar Avatar

Hey guys:
First of all, happy new year. I hope you have spent a nice holidays.
I was wondering if you are going to update the Ansistrano tutorial using Symfony 5. This has been one of the most useful courses for me and I think I am probably missing something in the configuration to explote all the potential in Symfony 5, maybe with the cache.
Are you planning to do this? Or at least put some little useful notes in the scripts?
Cesar

Reply

Hey Cesar!

Happy new year to you too! We did have nice holidays - I always enjoy a bit of quiet around the new year :).

We don't currently have any plans to update this course to Symfony 5, or, it's at least not a priority. But yes, we *do* typically like to add useful notes to tutorials to help extend their usefulness. This might be an opportunity for that!

> I am probably missing something in the configuration to explote all the potential in Symfony 5, maybe with the cache.

What makes you think that you are missing some Symfony 5 potential? Are you seeing errors? Poor performance? Something else?

Let me know :).

Cheers!

Reply
Cesar Avatar

Hey Ryan
Thanks for answering!
Sometimes I have errors in the deployment in the part of composer dependencies or in warming up the cache. I didn't identify why but the funny thing is when I run the deploy playbook again, it works perfectly. I don't have the type of errors now but next time I will probably tell you with more details.
My question was more a general wonder. For example, some tweaks to the code like you did in chapter 16. I am not an expert, I just follow your tutorials and things work. That's why I like Symfony.
Cheers.
Cesar

Reply

Hey Cesar!

> Sometimes I have errors in the deployment in the part of composer dependencies or in warming up the cache. I didn't identify why but the funny thing is when I run the deploy playbook again, it works perfectly

Got it :). Let us know if/when you see those error again - it's always possible it's something we could address with a note on the tutorial.

> My question was more a general wonder. For example, some tweaks to the code like you did in chapter 16.

Yea... obviously it's hard to think of *all* the possible things that might be different on a Symfony 5.2 app for this tutorial, but not a *lot* of things come to mind... at least not major things. The cache topic you linked to is actually one of the spots that "has" changed the most of the past few years. But actually, that chapter (I believe) is still accurate: if you warm up your cache AND have apcu or Opcache, then nothing "should" be written to your filesystem. That is still true - it had only just recently "become" true when we made this tutorial.

Anyways, that's not a complete answer, but I hope it helps :). Obviously there will be other differences like parameters.yml vs .env files, though it shouldn't make a huge difference. On that topic, I probably would now use Symfony's secrets system instead of Ansible's vault... just because it would make your life a bit easier.

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

While the fundamentals of Ansistrano haven't changed, this tutorial is built using Symfony 3, which has significant differences versus Symfony 4 and later.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "doctrine/doctrine-bundle": "^1.6", // 1.6.8
        "doctrine/orm": "^2.5", // v2.7.2
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "sensio/distribution-bundle": "^5.0.19", // v5.0.20
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.26
        "symfony/monolog-bundle": "^3.1.0", // v3.1.0
        "symfony/polyfill-apcu": "^1.0", // v1.4.0
        "symfony/swiftmailer-bundle": "^2.3.10", // v2.6.3
        "symfony/symfony": "3.3.*", // v3.3.5
        "twig/twig": "^1.0||^2.0", // v1.34.4
        "doctrine/doctrine-migrations-bundle": "^1.2", // v1.2.1
        "predis/predis": "^1.1", // v1.1.1
        "composer/package-versions-deprecated": "^1.11" // 1.11.99
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.6
        "symfony/phpunit-bridge": "^3.0", // v3.3.5
        "doctrine/data-fixtures": "^1.1", // 1.3.3
        "hautelook/alice-bundle": "^1.3" // v1.4.1
    }
}

What Ansible libraries does this tutorial use?

# ansible/requirements.yml
-
    src: DavidWittman.redis
    version: 1.2.4
-
    src: ansistrano.deploy
    version: 2.7.0
-
    src: ansistrano.rollback
    version: 2.0.1
userVoice