Deploying a Simple Rails App with Ansible

Ruby on Rails is quickly becoming my framework of choice for my personal websites and projects. It's a pleasure to work with and has been easy to learn. But no framework is without its challenges. One of those challenges is of course deploying the app to a server. There are a lot of options for hosting and deploying a Rails app. But, I like to run my own servers which means I have to also take care of deploying to those servers. I'd prefer to be deploying images to AWS ECS but I don't need that kind of infrastructure for my personal website. It's just a blog it can suffer seconds of downtime when I deploy updates. So my approach these days is to use Ansible to handle the deploy steps. 


---
- name: Deploy Rails
  hosts: app.roylindauer.com
  
  vars:
    branch: "main"
    homedir: "/myapp"

  handlers:
    - name: restart workers
      become: true
      become_user: root
      service: 
        name: rails-workers
        state: restarted

    - name: restart web
      become: true
      become_user: root
      service: 
        name: rails-web
        state: restarted

    - name: restart nginx
      become: true
      become_user: root
      service: 
        name: nginx
        state: restarted

  tasks:
    - name: Get Latest Source
      git: 
        repo: git@github.com:roylindauer/roylindauer.com.git
        dest: /myapp
        update: yes
        version: "{{ branch }}"
        force: yes
      notify:
        - restart workers

    - name: Bundle Install
      shell:
        chdir: "{{ homedir }}"
        cmd: "bin/bundle install --without development --path vendor/bundle"

    - name: Update Yarn
      shell:
        chdir: "{{ homedir }}"
        cmd: "bin/yarn install"
    
    - name: Check for Pending Migrations
      shell:
        chdir: "{{ homedir }}"
        cmd: "bin/rails db:migrate:status | grep -e '^  down' | wc -l"
      register: pending_migrations

    - debug:
        msg: "Pending Migrations: {{ pending_migrations['stdout'] }}"

    - name: Stop Workers
      become: true
      become_user: root
      service: 
        name: rails-workers
        state: stopped
      when: pending_migrations['stdout'] != '0'

    - name: DB Migrations & Restart Services
      shell:
        chdir: "{{ homedir }}"
        cmd: "bin/rake db:migrate"
      when: pending_migrations['stdout'] != '0'
      notify:
        - restart workers

    - name: Webpack
      shell:
        chdir: "{{ homedir }}"
        cmd: "bin/webpack"
      notify:
        - restart web
        - restart nginx

    - name: Precompile Assets
      shell:
        chdir: "{{ homedir }}"
        cmd: "bin/rails assets:precompile"
      notify:
        - restart web
        - restart nginx

Basically, do a git pull, update gems, then check for pending migrations. If there are migrations, stop workers, run migrations. After that compile assets, then finally restart services. 

For hosting a simple rails app on a single server this sort of deploy works very well.