Rolling back to previous release #9

thelucid opened this Issue Jun 12, 2012 · 26 comments

I am currently using Vlad and Mina looks like a much cleaner alternative as Vlad isn't maintained much anymore.

I might be missing something obvious but is there a way to roll back to a previous release?


Not yet. But it's next in our list:


Great, thanks.

I must say, I've just switched my first project to Mina from Vlad which was largely painless and the deployment is much simpler all in all. Many thanks for a great project.

One other problem I ran into was when some existing Vlad releases existed and I ran mina deploy:cleanup, it completely blitzed my current release, leaving no current release... this was a bit of a showstopper and deploys wouldn't work until I manually cleaned up the releases directory.

One other small niggle is that I'm not keen on the to :launch etc. syntax, it's not entirely clear what is going on... maybe just an explicit method that excepts a block would be better e.g.

task :deploy do
  deploy do
    # Put things that prepare the empty release folder here.
    # Commands queued here will be ran on a new release directory.
    invoke :'git:clone'
    invoke :'bundle:install'

    # These are instructions to start the app after it's been prepared.
    restart do
      queue 'touch tmp/restart.txt'

    # This optional block defines how a broken release should be cleaned up.
    clean do
      queue 'log "failed deployment"'
mina-deploy member

re: the :launch syntax:

that's my invention and I hate it myself! we've been toying with the idea of changing it to simply:

task :deploy do
  deploy do
    invoke :'git:clone'
    invoke :'other:things:here'

task :restart do
  queue "sudo service myapp restart"

... that is, simply assuming that the :restart task is the way to restart it. This way, you also get a bonus of getting to implement mina restart in a nice, conveniently DRY manner.

Comments and suggestions welcome.


Yes, much prefer that. I guess the same could be true for the "clean" block i.e. this could also become it's own task?

I'm trying to think of a way to remove the "deploy" block also to save on the additional nesting as essentially all it does it run the "" stuff after yielding the block. Maybe the stuff in "" could be made into another standard task (I'm thinking "update" like Vlad does), it could then use the queue method etc. to run all those bits. Just a thought.

Once everything is a standard task, the "deploy" task could be as simple as:

task :deploy => [:'git:clone', :'bundle:install', :'rails:db_migrate', :'other:stuff', :update, :restart]

I would actually like to see simpler task names as whichever scm you are using, there will be a clone/checkout stage, some kind of database migration is usually needed whichever language/framework you are using (could be blank task by default) and the "bundle:install" task could be the default for the "bundle" namespace... we would end up with something like:

task :deploy => [:clone, :bundle, :migrate, :update, :restart]

Much cleaner... most or if not all of this can be achieved without breaking the existing functionality, what do you think? I'm happy to have a crack at this if you are in agreement.

I will be writing a blog post on Mina soon over at as I'm loving it compared to Vlad or Capistrano so might hold off for a few days if there are likely to be implementation changes.

mina-deploy member
task :deploy => [:'git:clone', :'bundle:install', :'rails:db_migrate', :'other:stuff', :update, :restart]

This Cap/Vlad-like syntax is not going to happen, sorry. Let me explain, though... it's part of what makes Mina special.

Right now, you can chain tasks in that way. For instance, you may do the following, which makes mina slow_deploy work the same as doing mina local:test deploy remote:test.

task :slow_deploy => [:'local:test', :deploy, :'remote:test']

However, deploying is something different. Most Mina tasks are merely used to queue lines of Bash code in. This concept is incredibly simple:

task :tail do
  queue "echo Checking error logs"
  queue "tail -f /var/www/error.log"

# invoking "mina tail" simply runs the following bash script remotely.
# You can verify this with "mina --simulate":
#     echo Checking error logs
#     tail -f /var/www/error.log

In a deployment, you have to wrap your invokes and queues inside a deploy do ... end block. This certain deploy script below illustrates the point:

task :deploy do
  deploy do
    queue "wget"
    queue "tar xvf my_latest_code.tgz"

Running this via --simulate tells you that it concatenates the code very differently:

#!/usr/bin/env sh
cd /var/www/
echo "-----> Creating build path..."
mkdir -p tmp/build-283943
echo "-----> Doing deploys..."
cd tmp/build-283943 &&
) && (
  tar xvf my_latest_code.tgz
) && (
  echo "----> Deploy succeeded!"
) || (
  echo "! Deploy failed, rolling back"
  rm -rf tmp/build-283943
  exit 256

...of course, the actual script is much longer, but I've shorted it to illustrate the point.

What happened here? The deploy do...end block captured your queued items and put them together differently by placing it into a deploy script boilerplate.

You will not be able to do this simply by task :deploy => [:one, :two, :three].

mina-deploy member

Also, I would say no on the shortening of task names. It provides no value and makes the abstraction deeper than it actually should be. Can you just swap SCM addons (from git to svn, for instance) and expect everything to work? Can you make another "app framework" addon that implements its own migrate + bundle (and so on) and swap them out at will? Probably not.

Renaming them to generic names (eg, migrate meaning to simply invoke any migrations it should do, versus rails:db_migrate which specifically invokes database migrations for rails projects) only gives us another layer of abstraction that's not necessary. Today, one glance at a deploy block lets you somewhat figure out what a deploy script actually does, whereas if they were generic names, you would have to look up the definitions for each name to figure out what each would do.


Fair enough.

Did you go with assuming a restart task instead of the launch block in the end? If so what about the clean block, couldn't this assume a clean task also?


Hi there,

Has any work been started regarding the rollback feature?


👍 for this feature! But it's quite easy to just DIY, right?


@rstacruz is there any ETA for this feature?

mina-deploy member



Bump this up. It's over 2 years and seems nobody put it a good care.
I think we should make this a simple

mina rollback

what it does it simply remove the last releast, re-point symlink to the previous releas.

And move on.


you can use this to roll back;

but be carefull; if you rollback more than the number of deployed releases; you will get NIL

desc "Roll backs the lates release"
task :rollback => :environment do

queue! %[echo "-----> Rolling back to previous release for instance: #{domain}"]

# remove latest release folder (active release)
queue %[echo "-----> Deleting active release : "]
queue %[ls "#{deploy_to}/releases" -Art | sort | tail -n 1]
queue! %[ls "#{deploy_to}/releases" -Art | sort | tail -n 1 | xargs -I active sudo rm -rf "#{deploy_to}/releases/active"]

# delete existing sym link and create a new symlink pointing to the previous release
queue %[echo "-----> Creating new symlink from the previous release: "]
queue %[ls "#{deploy_to}/releases" -Art | sort | tail -n 1]
queue! %[sudo rm "#{deploy_to}/current"]
queue! %[ls -Art "#{deploy_to}/releases" | sort | tail -n 1 | xargs -I active sudo ln -s "#{deploy_to}/releases/active" "#{deploy_to}/current"]



Here's an improved version of the above script. It switches the release atomically and doesn't use sudo.

desc "Rolls back the latest release"
task :rollback => :environment do
  queue! %[echo "-----> Rolling back to previous release for instance: #{domain}"]

  # Delete existing sym link and create a new symlink pointing to the previous release
  queue %[echo -n "-----> Creating new symlink from the previous release: "]
  queue %[ls "#{deploy_to}/releases" -Art | sort | tail -n 2 | head -n 1]
  queue! %[ls -Art "#{deploy_to}/releases" | sort | tail -n 2 | head -n 1 | xargs -I active ln -nfs "#{deploy_to}/releases/active" "#{deploy_to}/current"]

  # Remove latest release folder (active release)
  queue %[echo -n "-----> Deleting active release: "]
  queue %[ls "#{deploy_to}/releases" -Art | sort | tail -n 1]
  queue! %[ls "#{deploy_to}/releases" -Art | sort | tail -n 1 | xargs -I active rm -rf "#{deploy_to}/releases/active"]

+1 would be a great addition for using mina/rails. Ive been using mina for 1.5 years now for sinatra apps. the first time now with rails. capistrano is a pain


what about migrations?


+1 for integrating it in mina. Anyway, thanks for the task @tmandry :)




+1, Thanks @tmandry !


+1 @tmandry nice task to take care of it for the time being!

@d4be4st d4be4st added a commit that closed this issue Mar 9, 2015
@d4be4st d4be4st fix #9 572f40e
@d4be4st d4be4st closed this in 572f40e Mar 9, 2015

For @d4be4st ... HURRAY \o/


👍 💃


Mina v0.3.3 released with rollback feature


In #9 (comment) use for sort sort -n to make sure the release numbers are not sorted as strings but as numbers.


Improvement for #9 (comment), #9 (comment), also using #9 (comment):

First: Tank you for sharing, I'd have gone nuts without the existing examples.
@elfuego @tmandry @mbahar

New feature: This one checks if a release to possibly roll back to does even exist.
If not, it does not execute the rollback.


mina deploy:rollback

does not do this, I'm afraid it's useless for me.


    desc "Rolls back the latest release"
    task :rollback => :environment do
        queue %[echo "-----> Rolling back to previous release for instance: #{domain}"]

        # ls command below must return two rows. first of the two contains the 2nd latest version.
        queue! %[last=`ls "#{deploy_to}/releases" -Art | sort -n | tail -n 2 | sed -n '1p'`]
        queue! %[current=`ls "#{deploy_to}/releases" -Art | sort -n | tail -n 2 | sed -n '2p'`]

        # sanity check: are both versions actual numbers? is the target folder available?
        queue! %[
            if [ ! -n "$last" ] || [ ! -n "$current" ] || [ ! -d "#{deploy_to}/releases/$last" ];
                echo "ERROR: No version to roll back to!";
                exit 1;

        queue %[echo "Moving from $current to $last"]

        # remove latest release folder (active release)
        queue! %[ls "#{deploy_to}/releases" -Art | sort -n | tail -n 1 | xargs -I active rm -rf "#{deploy_to}/releases/active"]

        # delete existing sym link and create a new symlink pointing to the previous release
        queue! %[rm "#{deploy_to}/current"]
        queue! %[ls -Art "#{deploy_to}/releases" | sort -n | tail -n 1 | xargs -I active ln -s "#{deploy_to}/releases/active" "#{deploy_to}/current"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment