Vagrant allows for provisioning of new guest instances using Shell but the script is always run in the
root context. Running as
root is great for global configuration without having to deal with
sudo. But, what do you do if you need to automatically configure the Vagrant login user during provisioning as well? That's not quite as straightforward as it might seem.
A skeleton of a typical project for me looks like:
. ├── Vagrantfile └── vagrant ├── provision.sh └── user-config.sh
vagrant directory is not mandatory but is a convention I've adapted to organize all the files I'll be using for Vagrant but aren't related to the application logic. Most importantly there are two files present. The
provision.sh is the file that holds all my global system configuration. The
user-config.sh holds configuration specific to the vagrant login user.
Vagrantfile will at a minimum be configured to use the Shell provisioner and kick off with the
Vagrant.configure('2') do |config| # -- snipped -- config.vm.provision :shell do |s| s.path = 'vagrant/provision.sh' end config.vm.synced_folder '.', '/home/vagrant/myapp' # -- snipped -- end
provision.sh is responsible for all my
root context actions like installing libraries or services such as Postgres, Redis, etc. This is all pretty typical of configuring a new system. What will be different though is that
provision.sh will also invoke the
user-config.sh as the
vagrant user to set up custom configuration for whatever it is that I'll be doing. Typically I use the
user-config.sh for installing RVM, installing my Ruby, Bundling Rubygems, then running the Rails and Rake tasks to setup my app.
An example of a minimal
provision.sh for an Ubuntu guest system looks like:
#!/bin/bash -x export DEBIAN_FRONTEND=noninteractive apt-get update apt-get -y install git # install stuff, configure env, etc su -c "source /home/vagrant/myapp/vagrant/user-config.sh" vagrant
Notice at the end that the
provision.sh script there's a
su command to execute the
user-config.sh as the
#!/bin/bash -x echo "export EDITOR=vim" >> $HOME/.bashrc # RVM echo "rvm_install_on_use_flag=1" >> $HOME/.rvmrc echo "rvm_project_rvmrc=1" >> $HOME/.rvmrc echo "rvm_trust_rvmrcs_flag=1" >> $HOME/.rvmrc curl -L https://get.rvm.io | bash -s stable --autolibs=4 source "$HOME/.rvm/scripts/rvm" [[ -s "$rvm_path/hooks/after_cd_bundle" ]] && chmod +x $rvm_path/hooks/after_cd_bundle rvm autolibs enable rvm requirements rvm reload _RUBY_VERSION="ruby-1.9.3" rvm install $_RUBY_VERSION rvm gemset create myapp rvm use $_RUBY_VERSION --default rvm use $_RUBY_VERSION@myapp cd ~/myapp gem update --system && gem update bundler bundle config --global jobs 3 bundle install bundle exec rake db:setup bundle exec rake db:test:prepare
This can hold anything you require for your app. This is just an example of what I'm using for an app I'm currently working on.
One caveat is that the Shell provisioner will try to run every time you
vagrant up. You'll need to change the way you start up Vagrant if you want to skip the provisioning step.
vagrant up --no-provision