├── .gitignore ├── CHANGELOG.md ├── MIT-LICENSE ├── bootstrap.yml ├── hosts.example ├── rails_deploy.yml ├── readme.md ├── roles ├── bootstrap │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ ├── deployuser.yml │ │ ├── devtools.yml │ │ ├── locale.yml │ │ └── main.yml │ └── templates │ │ └── etc │ │ └── timezone ├── capistrano │ ├── tasks │ │ └── main.yml │ └── templates │ │ └── env.j2 ├── crons │ └── tasks │ │ └── main.yml ├── nginx-passenger │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── templates │ │ ├── app.conf.j2 │ │ └── nginx.conf.j2 ├── nodejs │ └── tasks │ │ └── main.yml ├── postgres │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ ├── templates │ │ ├── pg_hba.conf.j2 │ │ └── postgresql.conf.j2 │ └── vars │ │ └── main.yml ├── preinstall-gems │ └── tasks │ │ └── main.yml ├── rbenv-ruby │ ├── files │ │ ├── 50_rbenv.bash │ │ ├── bash_50_rbenv │ │ └── gemrc │ └── tasks │ │ ├── common-post.yml │ │ ├── main.yml │ │ └── ruby-install.yml ├── rbenv │ ├── files │ │ ├── 50_rbenv.bash │ │ ├── bash_50_rbenv │ │ └── gemrc │ ├── handlers │ │ ├── main.yml │ │ └── rbenv-configure.yml │ └── tasks │ │ ├── common-post.yml │ │ ├── common-prereqs.yml │ │ ├── main.yml │ │ └── rbenv-setup.yml ├── redis │ └── tasks │ │ └── main.yml ├── ruby-multi │ ├── meta │ │ └── main.yml │ └── tasks │ │ └── main.yml ├── security │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ ├── fail2ban.yml │ │ ├── main.yml │ │ └── ufw.yml │ └── templates │ │ ├── jail.local.j2 │ │ └── ufw-ssh.conf.j2 └── swapfile │ ├── handlers │ └── main.yml │ └── tasks │ └── main.yml └── vars └── example.yml /.gitignore: -------------------------------------------------------------------------------- 1 | vars/all.yml 2 | hosts 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 23rd Aug 2015 2 | 3 | - Moved repo from personal account to my business Alternate Labs Ltd. 4 | 5 | # 2nd Aug 2015 6 | 7 | - Upgrade PostgreSQL to 9.4 8 | - Install redis by default 9 | 10 | # 26th Apr 2015 11 | 12 | - Update passenger_set_env for phusion passenger 5 13 | 14 | # 9th Feb 2015 15 | 16 | - Add configurable cron jobs to the server 17 | - Add libffi-dev for installing ruby 2.2.0 18 | - Allow multiple SSH keys to be added to the server rather than just one 19 | - nginx tweaks for large cookies 20 | - Install imagemagick by default 21 | - Some passenger configuration for `passenger_min_instances`, `passenger_pre_start` and `passenger_max_pool_size` 22 | - Ensure `HTTP_X_FORWARDED_FOR` and `HTTP_X_REAL_IP` headers are set 23 | 24 | # 30th Jan 2015 25 | 26 | - Tuning PostgreSQL to optimise for a 1GB vps server 27 | - Add swapfile to server 28 | - Setup basic UFW rules 29 | - Setup fail2ban 30 | 31 | # 2nd Jan 2015 32 | 33 | Initial launch 34 | 35 | - Deploys rails servers with rbenv, node and postgres 36 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015 Alternate Labs Ltd 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /bootstrap.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: appservers 3 | vars_files: 4 | - vars/all.yml 5 | roles: 6 | - bootstrap 7 | -------------------------------------------------------------------------------- /hosts.example: -------------------------------------------------------------------------------- 1 | [bootstrap] 2 | 127.0.0.1 ansible_ssh_user=root 3 | 4 | [appservers] 5 | 127.0.0.1 ansible_ssh_user=deploy 6 | -------------------------------------------------------------------------------- /rails_deploy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: appservers 3 | sudo: true 4 | vars_files: 5 | - vars/all.yml 6 | roles: 7 | - swapfile 8 | - security 9 | - capistrano 10 | - rbenv 11 | - ruby-multi 12 | - preinstall-gems 13 | - nodejs 14 | - nginx-passenger 15 | - postgres 16 | - redis 17 | - crons 18 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Rails deploy 2 | 3 | Ansible provisioning to setup a pre-configured Ruby/Rails server that contains the following: 4 | 5 | - Installs ruby via rbenv 6 | - Installs node.js for asset pipeline 7 | - Nginx + phusion passenger for serving your app 8 | - PostgreSQL + Redis databases 9 | - Setup some cron jobs to run rake tasks 10 | - Sets up ufw and fail2ban for basic security 11 | - Sets up swapon for when you run out of memory 12 | - Checkout the [changelog](CHANGELOG.md) for recent additions 13 | 14 | ## Prerequisites 15 | 16 | - [Install ansible](http://docs.ansible.com/intro_installation.html) on your system 17 | - Setup a Ubuntu 14.04 VPS server. Try a [digital ocean](https://digitalocean.com) 1gb droplet (would recommend at least 1GB memory). 18 | - SSH into the server as root and ensure that is working from your local machine. 19 | 20 | ## Usage 21 | 22 | For each server you want to setup clone this repo. This allows you to keep the configuration around and deploy again at any time, or deploy multiple servers for some horizontal scailing (in this instance you will need a shared database server). 23 | 24 | ```sh 25 | # Copy the example vars file over to vars/all.yml 26 | $ cp vars/example.yml vars/all.yml 27 | 28 | # Copy the example hosts file 29 | $ cp hosts.example hosts 30 | ``` 31 | 32 | Edit `./vars/all.yml` to meet your requirements. Replace the 127.0.0.1 IP address in `./hosts` with your servers IP 33 | 34 | ```sh 35 | # Run the initial boostrap for your server (sets up deploy user etc) 36 | $ ansible-playbook -i hosts bootstrap.yml --user root --ask-pass 37 | 38 | # Install all the things 39 | $ ansible-playbook -i hosts rails_deploy.yml --user deploy 40 | ``` 41 | 42 | ### Allow server to access source code 43 | 44 | First up you’ll need to allow your server access to wherever your source code is hosted, get the public SSH key generated on your serve by running a command like: 45 | 46 | ```sh 47 | # Remember to substitue your servers IP (and the deploy username if you altered it) 48 | $ ssh deploy@127.0.0.1 -t cat /home/deploy/.ssh/id_rsa.pub 49 | ``` 50 | 51 | Next you’ll want to add this key to your ssh keys on github (or bitbucket or whatever git host you use) 52 | 53 | ### Deploying your code 54 | 55 | This is designed to work out of the box with capistrano for deploys. You’ll want to setup capistrano like so: 56 | 57 | #### Gemfile 58 | 59 | Add the following gems to your Gemfile and run `bundle`. 60 | 61 | ```ruby 62 | gem 'dotenv-rails', '~> 1.0.2' 63 | 64 | group :development do 65 | gem 'capistrano', '~> 3.1' 66 | gem 'capistrano-rails', '~> 1.1' 67 | gem 'capistrano-rbenv', '~> 2.0' 68 | end 69 | ``` 70 | 71 | Once installed run `cap install` to create capistrano configuration files and then edit each file below. 72 | 73 | #### ./Capfile 74 | 75 | Next up make sure the following lines are uncommented from your `Capfile`. 76 | 77 | ```ruby 78 | require 'capistrano/rbenv' 79 | require 'capistrano/bundler' 80 | require 'capistrano/rails/assets' 81 | require 'capistrano/rails/migrations' 82 | ``` 83 | 84 | #### ./config/deploy.rb 85 | 86 | ```ruby 87 | # config valid only for Capistrano 3.1 88 | lock '3.2.1' 89 | 90 | set :application, 'APP_NAME' 91 | set :deploy_to, '/var/www/app' # Change this to match your deploy_dir in vars/app.yml 92 | set :repo_url, 'YOUR_GITHUB_URL' 93 | set :keep_releases, 3 94 | set :log_level, :info 95 | set :linked_files, %w{.env} 96 | set :migration_role, :app 97 | set :rbenv_type, :user 98 | set :rbenv_ruby, '2.1.5' 99 | 100 | namespace :deploy do 101 | desc 'Restart application' 102 | task :restart do 103 | on roles(:app), in: :sequence, wait: 3 do 104 | execute :touch, release_path.join('tmp/restart.txt') 105 | end 106 | end 107 | 108 | after :publishing, :restart 109 | end 110 | 111 | namespace :rails do 112 | desc 'Open the rails console on the primary remote server' 113 | task :console do 114 | on roles(:app), primary: true do |host| 115 | command = "cd #{deploy_to}/current && /home/#{host.user}/.rbenv/shims/bundle exec rails console #{fetch(:stage)}" 116 | exec "ssh -l #{host.user} #{host.hostname} -p #{host.port || 22} -t 'cd #{deploy_to}/current && #{command}'" 117 | end 118 | end 119 | end 120 | ``` 121 | 122 | #### ./config/deploy/production.rb 123 | 124 | Replace `127.0.0.1` with your servers IP address and update the user if you changed it from the defaults. 125 | 126 | ```ruby 127 | server '127.0.0.1', user: 'deploy', roles: %w{web app} 128 | ``` 129 | 130 | ## TODO 131 | 132 | - SSL Support 133 | - Common exports like EDITOR and RAILS_ENV for running rake tasks etc 134 | -------------------------------------------------------------------------------- /roles/bootstrap/handlers/main.yml: -------------------------------------------------------------------------------- 1 | - name: update tzdata 2 | command: /usr/sbin/dpkg-reconfigure --frontend noninteractive tzdata 3 | 4 | - name: Restart sshd 5 | action: service name=ssh state=restarted 6 | -------------------------------------------------------------------------------- /roles/bootstrap/tasks/deployuser.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # originally from: https://github.com/phred/5minbootstrap/ 3 | 4 | - name: Add deployment user 5 | action: 'user name={{ deploy_user }} password={{ deploy_password }} generate_ssh_key=yes shell=/bin/bash' 6 | 7 | - name: Add authorized deploy keys 8 | action: "authorized_key user={{ deploy_user }} key=\"{{ lookup('file', item) }}\"" 9 | with_items: ssh_public_key_files 10 | 11 | - name: Remove sudo group rights 12 | action: lineinfile dest=/etc/sudoers regexp="^%sudo" state=absent 13 | 14 | - name: Add deploy user to sudoers 15 | action: 'lineinfile dest=/etc/sudoers regexp="{{ deploy_user }} ALL" line="{{ deploy_user }} ALL=(ALL) NOPASSWD: ALL" state=present' 16 | 17 | - name: Disallow root SSH access 18 | action: lineinfile dest=/etc/ssh/sshd_config regexp="^PermitRootLogin" line="PermitRootLogin no" state=present 19 | notify: Restart sshd 20 | 21 | - name: Disallow password authentication 22 | action: lineinfile dest=/etc/ssh/sshd_config regexp="^PasswordAuthentication" line="PasswordAuthentication no" state=present 23 | notify: Restart sshd 24 | -------------------------------------------------------------------------------- /roles/bootstrap/tasks/devtools.yml: -------------------------------------------------------------------------------- 1 | - name: Update APT package cache 2 | action: apt update_cache=yes 3 | 4 | - name: Install basic dev tools 5 | action: apt pkg=build-essential state=installed 6 | -------------------------------------------------------------------------------- /roles/bootstrap/tasks/locale.yml: -------------------------------------------------------------------------------- 1 | # only in ansible 1.6 2 | # - name: Generate en_GB.UTF-8 locale 3 | # locale_gen: name=en_GB.UTF-8 state=present 4 | 5 | - name: set locale to {{locale}} 6 | command: /usr/sbin/update-locale LANG={{locale}} LC_ALL={{locale}} 7 | 8 | - name: set /etc/localtime to {{ timezone }} 9 | command: /bin/cp /usr/share/zoneinfo/{{ timezone }} /etc/localtime 10 | 11 | - name: set /etc/timezone to {{ timezone }} 12 | template: src=etc/timezone dest=/etc/timezone 13 | notify: update tzdata 14 | -------------------------------------------------------------------------------- /roles/bootstrap/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - include: deployuser.yml tags=deployuser 2 | - include: locale.yml tags=locale 3 | - include: devtools.yml tags=devtools 4 | -------------------------------------------------------------------------------- /roles/bootstrap/templates/etc/timezone: -------------------------------------------------------------------------------- 1 | {{ timezone }} 2 | -------------------------------------------------------------------------------- /roles/capistrano/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create app deploy dir 3 | file: 'path={{ deploy_dir }} state=directory owner={{ deploy_user }} group={{ deploy_user }}' 4 | 5 | - name: Create folder for nginx logs 6 | file: 'path={{ deploy_dir }}/log state=directory owner={{ deploy_user }} group={{ deploy_user }}' 7 | 8 | - name: Create capistrano shared folder 9 | file: 'path={{ deploy_dir }}/shared state=directory owner={{ deploy_user }} group={{ deploy_user }}' 10 | 11 | - name: write .env file for app environment variables 12 | template: 'src=env.j2 dest={{ deploy_dir }}/shared/.env owner={{ deploy_user }} group={{ deploy_user }}' 13 | -------------------------------------------------------------------------------- /roles/capistrano/templates/env.j2: -------------------------------------------------------------------------------- 1 | DATABASE_URL=postgres://{{ pg.user }}:{{ pg.pass }}@127.0.0.1:5432/{{ pg.db }} 2 | {% for envar in envars %} 3 | {{ envar }} 4 | {% endfor %} 5 | -------------------------------------------------------------------------------- /roles/crons/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Add cron jobs 2 | cron: user="{{ deploy_user }}" name="{{ item.name }}" minute="{{ item.minute }}" hour="{{ item.hour }}" job="{{ item.job }}" 3 | with_items: cron_jobs 4 | -------------------------------------------------------------------------------- /roles/nginx-passenger/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: nginx restart 3 | service: name=nginx state=restarted 4 | 5 | - name: nginx update-rc.d 6 | shell: update-rc.d nginx defaults 7 | 8 | - name: nginx reload 9 | service: name=nginx state=reloaded 10 | -------------------------------------------------------------------------------- /roles/nginx-passenger/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Remove apache2 2 | apt: name=apache2 state=absent 3 | 4 | - name: install dependencies 5 | action: apt pkg={{item}} state=installed 6 | with_items: 7 | - libv8-dev 8 | - python-pycurl 9 | - libcurl4-openssl-dev 10 | 11 | - name: Add key for passenger 12 | apt_key: url=http://keyserver.ubuntu.com/pks/lookup?op=get&fingerprint=on&search=0xAC40B2F7 state=present 13 | 14 | - name: Apt via https 15 | apt: name=apt-transport-https state=present 16 | 17 | - name: Apt add passenger to list 18 | apt_repository: repo='deb https://oss-binaries.phusionpassenger.com/apt/passenger {{ansible_distribution_release}} main' state=present update_cache=yes 19 | 20 | - name: Install nginx 21 | action: apt pkg={{ item }} state=installed 22 | with_items: 23 | - nginx-extras 24 | - passenger 25 | 26 | - name: Create global nginx configs dir 27 | file: path=/etc/nginx/global state=directory 28 | 29 | - name: Remove the default app, if exists 30 | command: rm -rf /etc/nginx/sites-enabled/default 31 | 32 | - name: write the nginx config files 33 | template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf 34 | notify: 35 | - nginx restart 36 | 37 | - name: write nginx app config file 38 | template: src=app.conf.j2 dest=/etc/nginx/sites-available/app.conf 39 | 40 | - name: put application live 41 | file: src=/etc/nginx/sites-available/app.conf dest=/etc/nginx/sites-enabled/app.conf owner=root group=root state=link 42 | notify: 43 | - nginx restart 44 | 45 | - name: start nginx 46 | service: name=nginx state=started 47 | -------------------------------------------------------------------------------- /roles/nginx-passenger/templates/app.conf.j2: -------------------------------------------------------------------------------- 1 | # Force non-www version of the site 2 | server { 3 | listen {{ nginx.port }}; 4 | server_name www.{{ deploy_hostname }}; 5 | return 301 http://{{ deploy_hostname }}$request_uri; 6 | } 7 | 8 | # Default server 9 | server { 10 | listen {{ nginx.port }} default_server; 11 | server_name {{ deploy_hostname }}; 12 | server_tokens off; 13 | root {{ deploy_dir }}/current/public; 14 | 15 | access_log {{ deploy_dir }}/log/nginx.access.log; 16 | error_log {{ deploy_dir }}/log/nginx.error.log; 17 | 18 | passenger_enabled on; 19 | passenger_min_instances {{ passenger.min_instances }}; 20 | passenger_app_env production; 21 | passenger_set_header HTTP_X_FORWARDED_FOR $proxy_add_x_forwarded_for; 22 | passenger_set_header HTTP_X_REAL_IP $proxy_add_x_forwarded_for; 23 | } 24 | -------------------------------------------------------------------------------- /roles/nginx-passenger/templates/nginx.conf.j2: -------------------------------------------------------------------------------- 1 | user {{ nginx.user }}; 2 | worker_processes {{ nginx.workers }}; 3 | pid /var/run/nginx.pid; 4 | 5 | events { 6 | worker_connections {{ nginx.worker_connections }}; 7 | multi_accept on; 8 | } 9 | 10 | http { 11 | 12 | passenger_root /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini; 13 | passenger_ruby /home/{{ deploy_user }}/.rbenv/shims/ruby; 14 | passenger_nodejs /usr/bin/node; 15 | passenger_default_user {{ deploy_user }}; 16 | passenger_friendly_error_pages off; 17 | passenger_max_pool_size {{ passenger.max_pool_size }}; 18 | passenger_pre_start {{ passenger.pre_start_url }}; 19 | 20 | 21 | ## 22 | # Basic Settings 23 | ## 24 | 25 | sendfile on; 26 | tcp_nopush on; 27 | tcp_nodelay on; 28 | types_hash_max_size 2048; 29 | server_tokens off; 30 | 31 | include /etc/nginx/mime.types; 32 | default_type text/html; 33 | 34 | ## 35 | # Buffers 36 | ## 37 | 38 | server_names_hash_bucket_size 64; 39 | client_body_buffer_size 10K; 40 | client_header_buffer_size 1k; 41 | client_max_body_size 20m; 42 | large_client_header_buffers 4 32k; 43 | 44 | ## 45 | # Timeouts 46 | ## 47 | 48 | client_body_timeout 12; 49 | client_header_timeout 12; 50 | keepalive_timeout 15; 51 | send_timeout 10; 52 | 53 | 54 | ## 55 | # Logging Settings 56 | ## 57 | 58 | access_log /var/log/nginx/access.log; 59 | error_log /var/log/nginx/error.log; 60 | 61 | ## 62 | # Gzip Settings 63 | ## 64 | 65 | gzip on; 66 | gzip_disable "msie6"; 67 | 68 | gzip_vary on; 69 | gzip_static on; 70 | gzip_comp_level 2; 71 | gzip_proxied any; 72 | gzip_min_length 1000; 73 | gzip_http_version 1.1; 74 | gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; 75 | 76 | ## 77 | # SSL config 78 | ## 79 | 80 | # Optimise time to first byte 81 | ssl_buffer_size 4k; 82 | 83 | # SSL session resumption 84 | ssl_session_cache shared:SSL:10m; 85 | ssl_session_timeout 10m; 86 | 87 | ## 88 | # Virtual Host Configs 89 | ## 90 | 91 | include /etc/nginx/conf.d/*.conf; 92 | include /etc/nginx/sites-enabled/*; 93 | } 94 | -------------------------------------------------------------------------------- /roles/nodejs/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Ensure Ubuntu Distro is Supported 3 | get_url: 4 | url='https://deb.nodesource.com/node/dists/{{ ansible_distribution_release }}/Release' 5 | dest=/dev/null 6 | register: distrosupported 7 | 8 | - name: Remove Old Chris Lea PPA 9 | apt_repository: 10 | repo='ppa:chris-lea/node.js' 11 | state=absent 12 | when: distrosupported|success 13 | 14 | - name: Remove Old Chris Lea Sources 15 | file: 16 | path='/etc/apt/sources.list.d/chris-lea-node_js-{{ ansible_distribution_release }}.list' 17 | state=absent 18 | when: distrosupported|success 19 | 20 | - name: Add Nodesource Keys 21 | apt_key: 22 | url=https://deb.nodesource.com/gpgkey/nodesource.gpg.key 23 | state=present 24 | 25 | - name: Add Nodesource Apt Sources List Deb 26 | apt_repository: 27 | repo='deb https://deb.nodesource.com/node {{ ansible_distribution_release }} main' 28 | state=present 29 | when: distrosupported|success 30 | 31 | - name: Add Nodesource Apt Sources List Deb Src 32 | apt_repository: 33 | repo='deb-src https://deb.nodesource.com/node {{ ansible_distribution_release }} main' 34 | state=present 35 | when: distrosupported|success 36 | 37 | - name: Install NodeJS 38 | apt: pkg=nodejs state=latest update_cache=true 39 | when: distrosupported|success 40 | -------------------------------------------------------------------------------- /roles/postgres/handlers/main.yml: -------------------------------------------------------------------------------- 1 | - name: postgresql restart 2 | action: service name=postgresql state=restarted 3 | sudo: true 4 | -------------------------------------------------------------------------------- /roles/postgres/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Install PostgreSQL dependencies 2 | action: apt pkg={{item}} state=latest 3 | with_items: 4 | - python-pycurl 5 | - python-psycopg2 6 | 7 | - name: Add PostgreSQL repo key 8 | apt_key: url=http://apt.postgresql.org/pub/repos/apt/ACCC4CF8.asc 9 | 10 | - name: Add PostgreSQL repo 11 | apt_repository: repo='deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main' 12 | 13 | - name: Install PostgreSQL 14 | apt: pkg=postgresql-9.4 state=latest update_cache=true 15 | 16 | - name: Restart PostgreSQL 17 | command: service postgresql restart 18 | 19 | - name: Copy valid pg_hba.conf 20 | template: src=pg_hba.conf.j2 dest=/etc/postgresql/9.4/main/pg_hba.conf 21 | notify: 22 | - postgresql restart 23 | 24 | - name: Copy valid postgresql.conf 25 | template: src=postgresql.conf.j2 dest=/etc/postgresql/9.4/main/postgresql.conf 26 | notify: 27 | - postgresql restart 28 | 29 | - name: Create deploy user db 30 | sudo_user: postgres 31 | postgresql_db: 'name={{ deploy_user }}' 32 | 33 | - name: Create deploy_user superuser 34 | sudo_user: postgres 35 | postgresql_user: 'name={{ deploy_user }} password=NULL role_attr_flags=SUPERUSER' 36 | 37 | - name: Create app db 38 | sudo_user: postgres 39 | postgresql_db: 'name={{ pg.db }}' 40 | 41 | - name: Create app db user 42 | sudo_user: postgres 43 | postgresql_user: 'name={{ pg.user }} password={{ pg.pass }}' 44 | 45 | - name: Grant all privs on app db to app db user 46 | sudo_user: postgres 47 | postgresql_privs: > 48 | db=postgres 49 | privs=ALL 50 | type=database 51 | obj={{ pg.db }} 52 | role={{ pg.user }} 53 | -------------------------------------------------------------------------------- /roles/postgres/templates/pg_hba.conf.j2: -------------------------------------------------------------------------------- 1 | local all postgres peer 2 | local all {{ deploy_user }} peer 3 | local all all md5 4 | host all all 127.0.0.1/32 md5 5 | -------------------------------------------------------------------------------- /roles/postgres/templates/postgresql.conf.j2: -------------------------------------------------------------------------------- 1 | # ----------------------------- 2 | # PostgreSQL configuration file 3 | # ----------------------------- 4 | # 5 | # This file consists of lines of the form: 6 | # 7 | # name = value 8 | # 9 | # (The "=" is optional.) Whitespace may be used. Comments are introduced with 10 | # "#" anywhere on a line. The complete list of parameter names and allowed 11 | # values can be found in the PostgreSQL documentation. 12 | # 13 | # The commented-out settings shown in this file represent the default values. 14 | # Re-commenting a setting is NOT sufficient to revert it to the default value; 15 | # you need to reload the server. 16 | # 17 | # This file is read on server startup and when the server receives a SIGHUP 18 | # signal. If you edit the file on a running system, you have to SIGHUP the 19 | # server for the changes to take effect, or use "pg_ctl reload". Some 20 | # parameters, which are marked below, require a server shutdown and restart to 21 | # take effect. 22 | # 23 | # Any parameter can also be given as a command-line option to the server, e.g., 24 | # "postgres -c log_connections=on". Some parameters can be changed at run time 25 | # with the "SET" SQL command. 26 | # 27 | # Memory units: kB = kilobytes Time units: ms = milliseconds 28 | # MB = megabytes s = seconds 29 | # GB = gigabytes min = minutes 30 | # h = hours 31 | # d = days 32 | 33 | 34 | #------------------------------------------------------------------------------ 35 | # FILE LOCATIONS 36 | #------------------------------------------------------------------------------ 37 | 38 | # The default values of these variables are driven from the -D command-line 39 | # option or PGDATA environment variable, represented here as ConfigDir. 40 | 41 | data_directory = '/var/lib/postgresql/9.4/main' # use data in another directory 42 | # (change requires restart) 43 | hba_file = '/etc/postgresql/9.4/main/pg_hba.conf' # host-based authentication file 44 | # (change requires restart) 45 | ident_file = '/etc/postgresql/9.4/main/pg_ident.conf' # ident configuration file 46 | # (change requires restart) 47 | 48 | # If external_pid_file is not explicitly set, no extra PID file is written. 49 | external_pid_file = '/var/run/postgresql/9.4-main.pid' # write an extra PID file 50 | # (change requires restart) 51 | 52 | 53 | #------------------------------------------------------------------------------ 54 | # CONNECTIONS AND AUTHENTICATION 55 | #------------------------------------------------------------------------------ 56 | 57 | # - Connection Settings - 58 | 59 | listen_addresses = '{{ pg.listen_addresses }}' # what IP address(es) to listen on; 60 | # comma-separated list of addresses; 61 | # defaults to 'localhost', '*' = all 62 | # (change requires restart) 63 | port = 5432 # (change requires restart) 64 | max_connections = {{ pg.max_connections }} 65 | # Note: Increasing max_connections costs ~400 bytes of shared memory per 66 | # connection slot, plus lock space (see max_locks_per_transaction). 67 | #superuser_reserved_connections = 3 # (change requires restart) 68 | unix_socket_directories = '/var/run/postgresql' # (change requires restart) 69 | #unix_socket_group = '' # (change requires restart) 70 | #unix_socket_permissions = 0777 # begin with 0 to use octal notation 71 | # (change requires restart) 72 | #bonjour = off # advertise server via Bonjour 73 | # (change requires restart) 74 | #bonjour_name = '' # defaults to the computer name 75 | # (change requires restart) 76 | 77 | # - Security and Authentication - 78 | 79 | #authentication_timeout = 1min # 1s-600s 80 | ssl = false # (change requires restart) 81 | #ssl_ciphers = 'ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH' # allowed SSL ciphers 82 | # (change requires restart) 83 | #ssl_renegotiation_limit = 512MB # amount of data between renegotiations 84 | #password_encryption = on 85 | #db_user_namespace = off 86 | 87 | # Kerberos and GSSAPI 88 | #krb_server_keyfile = '' 89 | #krb_srvname = 'postgres' # (Kerberos only) 90 | #krb_caseins_users = off 91 | 92 | # - TCP Keepalives - 93 | # see "man 7 tcp" for details 94 | 95 | #tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds; 96 | # 0 selects the system default 97 | #tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds; 98 | # 0 selects the system default 99 | #tcp_keepalives_count = 0 # TCP_KEEPCNT; 100 | # 0 selects the system default 101 | 102 | 103 | #------------------------------------------------------------------------------ 104 | # RESOURCE USAGE (except WAL) 105 | #------------------------------------------------------------------------------ 106 | 107 | # - Memory - 108 | 109 | shared_buffers = {{ pg.shared_buffers }} # min 128kB 110 | # (change requires restart) 111 | temp_buffers = 8MB # min 800kB 112 | #max_prepared_transactions = 0 # zero disables the feature 113 | # (change requires restart) 114 | # Note: Increasing max_prepared_transactions costs ~600 bytes of shared memory 115 | # per transaction slot, plus lock space (see max_locks_per_transaction). 116 | # It is not advisable to set max_prepared_transactions nonzero unless you 117 | # actively intend to use prepared transactions. 118 | #work_mem = 1MB # min 64kB 119 | #maintenance_work_mem = 16MB # min 1MB 120 | #max_stack_depth = 2MB # min 100kB 121 | 122 | # - Kernel Resource Usage - 123 | 124 | #max_files_per_process = 1000 # min 25 125 | # (change requires restart) 126 | #shared_preload_libraries = '' # (change requires restart) 127 | 128 | # - Cost-Based Vacuum Delay - 129 | 130 | #vacuum_cost_delay = 0ms # 0-100 milliseconds 131 | #vacuum_cost_page_hit = 1 # 0-10000 credits 132 | #vacuum_cost_page_miss = 10 # 0-10000 credits 133 | #vacuum_cost_page_dirty = 20 # 0-10000 credits 134 | #vacuum_cost_limit = 200 # 1-10000 credits 135 | 136 | # - Background Writer - 137 | 138 | #bgwriter_delay = 200ms # 10-10000ms between rounds 139 | #bgwriter_lru_maxpages = 100 # 0-1000 max buffers written/round 140 | #bgwriter_lru_multiplier = 2.0 # 0-10.0 multipler on buffers scanned/round 141 | 142 | # - Asynchronous Behavior - 143 | 144 | #effective_io_concurrency = 1 # 1-1000. 0 disables prefetching 145 | 146 | 147 | #------------------------------------------------------------------------------ 148 | # WRITE AHEAD LOG 149 | #------------------------------------------------------------------------------ 150 | 151 | # - Settings - 152 | 153 | #wal_level = minimal # minimal, archive, or hot_standby 154 | # (change requires restart) 155 | #fsync = on # turns forced synchronization on or off 156 | #synchronous_commit = on # synchronization level; on, off, or local 157 | #wal_sync_method = fsync # the default is the first option 158 | # supported by the operating system: 159 | # open_datasync 160 | # fdatasync (default on Linux) 161 | # fsync 162 | # fsync_writethrough 163 | # open_sync 164 | #full_page_writes = on # recover from partial page writes 165 | #wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers 166 | # (change requires restart) 167 | #wal_writer_delay = 200ms # 1-10000 milliseconds 168 | 169 | #commit_delay = 0 # range 0-100000, in microseconds 170 | #commit_siblings = 5 # range 1-1000 171 | 172 | # - Checkpoints - 173 | 174 | checkpoint_segments = 10 # in logfile segments, min 1, 16MB each 175 | #checkpoint_timeout = 5min # range 30s-1h 176 | checkpoint_completion_target = 0.9 # checkpoint target duration, 0.0 - 1.0 177 | #checkpoint_warning = 30s # 0 disables 178 | 179 | # - Archiving - 180 | 181 | #archive_mode = off # allows archiving to be done 182 | # (change requires restart) 183 | #archive_command = '' # command to use to archive a logfile segment 184 | #archive_timeout = 0 # force a logfile segment switch after this 185 | # number of seconds; 0 disables 186 | 187 | 188 | #------------------------------------------------------------------------------ 189 | # REPLICATION 190 | #------------------------------------------------------------------------------ 191 | 192 | # - Master Server - 193 | 194 | # These settings are ignored on a standby server 195 | 196 | #max_wal_senders = 0 # max number of walsender processes 197 | # (change requires restart) 198 | #wal_sender_delay = 1s # walsender cycle time, 1-10000 milliseconds 199 | #wal_keep_segments = 0 # in logfile segments, 16MB each; 0 disables 200 | #vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed 201 | #replication_timeout = 60s # in milliseconds; 0 disables 202 | #synchronous_standby_names = '' # standby servers that provide sync rep 203 | # comma-separated list of application_name 204 | # from standby(s); '*' = all 205 | 206 | # - Standby Servers - 207 | 208 | # These settings are ignored on a master server 209 | 210 | #hot_standby = off # "on" allows queries during recovery 211 | # (change requires restart) 212 | #max_standby_archive_delay = 30s # max delay before canceling queries 213 | # when reading WAL from archive; 214 | # -1 allows indefinite delay 215 | #max_standby_streaming_delay = 30s # max delay before canceling queries 216 | # when reading streaming WAL; 217 | # -1 allows indefinite delay 218 | #wal_receiver_status_interval = 10s # send replies at least this often 219 | # 0 disables 220 | #hot_standby_feedback = off # send info from standby to prevent 221 | # query conflicts 222 | 223 | 224 | #------------------------------------------------------------------------------ 225 | # QUERY TUNING 226 | #------------------------------------------------------------------------------ 227 | 228 | # - Planner Method Configuration - 229 | 230 | #enable_bitmapscan = on 231 | #enable_hashagg = on 232 | #enable_hashjoin = on 233 | #enable_indexscan = on 234 | #enable_material = on 235 | #enable_mergejoin = on 236 | #enable_nestloop = on 237 | #enable_seqscan = on 238 | #enable_sort = on 239 | #enable_tidscan = on 240 | 241 | # - Planner Cost Constants - 242 | 243 | #seq_page_cost = 1.0 # measured on an arbitrary scale 244 | #random_page_cost = 4.0 # same scale as above 245 | #cpu_tuple_cost = 0.01 # same scale as above 246 | #cpu_index_tuple_cost = 0.005 # same scale as above 247 | #cpu_operator_cost = 0.0025 # same scale as above 248 | #effective_cache_size = 128MB 249 | 250 | # - Genetic Query Optimizer - 251 | 252 | #geqo = on 253 | #geqo_threshold = 12 254 | #geqo_effort = 5 # range 1-10 255 | #geqo_pool_size = 0 # selects default based on effort 256 | #geqo_generations = 0 # selects default based on effort 257 | #geqo_selection_bias = 2.0 # range 1.5-2.0 258 | #geqo_seed = 0.0 # range 0.0-1.0 259 | 260 | # - Other Planner Options - 261 | 262 | #default_statistics_target = 100 # range 1-10000 263 | #constraint_exclusion = partition # on, off, or partition 264 | #cursor_tuple_fraction = 0.1 # range 0.0-1.0 265 | #from_collapse_limit = 8 266 | #join_collapse_limit = 8 # 1 disables collapsing of explicit 267 | # JOIN clauses 268 | 269 | 270 | #------------------------------------------------------------------------------ 271 | # ERROR REPORTING AND LOGGING 272 | #------------------------------------------------------------------------------ 273 | 274 | # - Where to Log - 275 | 276 | #log_destination = 'stderr' # Valid values are combinations of 277 | # stderr, csvlog, syslog, and eventlog, 278 | # depending on platform. csvlog 279 | # requires logging_collector to be on. 280 | 281 | # This is used when logging to stderr: 282 | #logging_collector = off # Enable capturing of stderr and csvlog 283 | # into log files. Required to be on for 284 | # csvlogs. 285 | # (change requires restart) 286 | 287 | # These are only used if logging_collector is on: 288 | #log_directory = 'pg_log' # directory where log files are written, 289 | # can be absolute or relative to PGDATA 290 | #log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern, 291 | # can include strftime() escapes 292 | #log_file_mode = 0600 # creation mode for log files, 293 | # begin with 0 to use octal notation 294 | #log_truncate_on_rotation = off # If on, an existing log file with the 295 | # same name as the new log file will be 296 | # truncated rather than appended to. 297 | # But such truncation only occurs on 298 | # time-driven rotation, not on restarts 299 | # or size-driven rotation. Default is 300 | # off, meaning append to existing files 301 | # in all cases. 302 | #log_rotation_age = 1d # Automatic rotation of logfiles will 303 | # happen after that time. 0 disables. 304 | #log_rotation_size = 10MB # Automatic rotation of logfiles will 305 | # happen after that much log output. 306 | # 0 disables. 307 | 308 | # These are relevant when logging to syslog: 309 | #syslog_facility = 'LOCAL0' 310 | #syslog_ident = 'postgres' 311 | 312 | #silent_mode = off # Run server silently. 313 | # DO NOT USE without syslog or 314 | # logging_collector 315 | # (change requires restart) 316 | 317 | 318 | # - When to Log - 319 | 320 | #client_min_messages = notice # values in order of decreasing detail: 321 | # debug5 322 | # debug4 323 | # debug3 324 | # debug2 325 | # debug1 326 | # log 327 | # notice 328 | # warning 329 | # error 330 | 331 | #log_min_messages = warning # values in order of decreasing detail: 332 | # debug5 333 | # debug4 334 | # debug3 335 | # debug2 336 | # debug1 337 | # info 338 | # notice 339 | # warning 340 | # error 341 | # log 342 | # fatal 343 | # panic 344 | 345 | #log_min_error_statement = error # values in order of decreasing detail: 346 | # debug5 347 | # debug4 348 | # debug3 349 | # debug2 350 | # debug1 351 | # info 352 | # notice 353 | # warning 354 | # error 355 | # log 356 | # fatal 357 | # panic (effectively off) 358 | 359 | #log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements 360 | # and their durations, > 0 logs only 361 | # statements running at least this number 362 | # of milliseconds 363 | 364 | 365 | # - What to Log - 366 | 367 | #debug_print_parse = off 368 | #debug_print_rewritten = off 369 | #debug_print_plan = off 370 | #debug_pretty_print = on 371 | #log_checkpoints = off 372 | #log_connections = off 373 | #log_disconnections = off 374 | #log_duration = off 375 | #log_error_verbosity = default # terse, default, or verbose messages 376 | #log_hostname = off 377 | log_line_prefix = '%t ' # special values: 378 | # %a = application name 379 | # %u = user name 380 | # %d = database name 381 | # %r = remote host and port 382 | # %h = remote host 383 | # %p = process ID 384 | # %t = timestamp without milliseconds 385 | # %m = timestamp with milliseconds 386 | # %i = command tag 387 | # %e = SQL state 388 | # %c = session ID 389 | # %l = session line number 390 | # %s = session start timestamp 391 | # %v = virtual transaction ID 392 | # %x = transaction ID (0 if none) 393 | # %q = stop here in non-session 394 | # processes 395 | # %% = '%' 396 | # e.g. '<%u%%%d> ' 397 | #log_lock_waits = off # log lock waits >= deadlock_timeout 398 | #log_statement = 'none' # none, ddl, mod, all 399 | #log_temp_files = -1 # log temporary files equal or larger 400 | # than the specified size in kilobytes; 401 | # -1 disables, 0 logs all temp files 402 | #log_timezone = '(defaults to server environment setting)' 403 | 404 | 405 | #------------------------------------------------------------------------------ 406 | # RUNTIME STATISTICS 407 | #------------------------------------------------------------------------------ 408 | 409 | # - Query/Index Statistics Collector - 410 | 411 | #track_activities = on 412 | #track_counts = on 413 | #track_functions = none # none, pl, all 414 | #track_activity_query_size = 1024 # (change requires restart) 415 | #update_process_title = on 416 | #stats_temp_directory = 'pg_stat_tmp' 417 | 418 | 419 | # - Statistics Monitoring - 420 | 421 | #log_parser_stats = off 422 | #log_planner_stats = off 423 | #log_executor_stats = off 424 | #log_statement_stats = off 425 | 426 | 427 | #------------------------------------------------------------------------------ 428 | # AUTOVACUUM PARAMETERS 429 | #------------------------------------------------------------------------------ 430 | 431 | #autovacuum = on # Enable autovacuum subprocess? 'on' 432 | # requires track_counts to also be on. 433 | #log_autovacuum_min_duration = -1 # -1 disables, 0 logs all actions and 434 | # their durations, > 0 logs only 435 | # actions running at least this number 436 | # of milliseconds. 437 | #autovacuum_max_workers = 3 # max number of autovacuum subprocesses 438 | # (change requires restart) 439 | #autovacuum_naptime = 1min # time between autovacuum runs 440 | #autovacuum_vacuum_threshold = 50 # min number of row updates before 441 | # vacuum 442 | #autovacuum_analyze_threshold = 50 # min number of row updates before 443 | # analyze 444 | #autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum 445 | #autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze 446 | #autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum 447 | # (change requires restart) 448 | #autovacuum_vacuum_cost_delay = 20ms # default vacuum cost delay for 449 | # autovacuum, in milliseconds; 450 | # -1 means use vacuum_cost_delay 451 | #autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for 452 | # autovacuum, -1 means use 453 | # vacuum_cost_limit 454 | 455 | 456 | #------------------------------------------------------------------------------ 457 | # CLIENT CONNECTION DEFAULTS 458 | #------------------------------------------------------------------------------ 459 | 460 | # - Statement Behavior - 461 | 462 | #search_path = '"$user",public' # schema names 463 | #default_tablespace = '' # a tablespace name, '' uses the default 464 | #temp_tablespaces = '' # a list of tablespace names, '' uses 465 | # only default tablespace 466 | #check_function_bodies = on 467 | #default_transaction_isolation = 'read committed' 468 | #default_transaction_read_only = off 469 | #default_transaction_deferrable = off 470 | #session_replication_role = 'origin' 471 | #statement_timeout = 0 # in milliseconds, 0 is disabled 472 | #vacuum_freeze_min_age = 50000000 473 | #vacuum_freeze_table_age = 150000000 474 | #bytea_output = 'hex' # hex, escape 475 | #xmlbinary = 'base64' 476 | #xmloption = 'content' 477 | 478 | # - Locale and Formatting - 479 | 480 | datestyle = 'iso, mdy' 481 | #intervalstyle = 'postgres' 482 | #timezone = '(defaults to server environment setting)' 483 | #timezone_abbreviations = 'Default' # Select the set of available time zone 484 | # abbreviations. Currently, there are 485 | # Default 486 | # Australia 487 | # India 488 | # You can create your own file in 489 | # share/timezonesets/. 490 | #extra_float_digits = 0 # min -15, max 3 491 | #client_encoding = sql_ascii # actually, defaults to database 492 | # encoding 493 | 494 | # These settings are initialized by initdb, but they can be changed. 495 | lc_messages = 'en_US.UTF-8' # locale for system error message 496 | # strings 497 | lc_monetary = 'en_US.UTF-8' # locale for monetary formatting 498 | lc_numeric = 'en_US.UTF-8' # locale for number formatting 499 | lc_time = 'en_US.UTF-8' # locale for time formatting 500 | 501 | # default configuration for text search 502 | default_text_search_config = 'pg_catalog.english' 503 | 504 | # - Other Defaults - 505 | 506 | #dynamic_library_path = '$libdir' 507 | #local_preload_libraries = '' 508 | 509 | 510 | #------------------------------------------------------------------------------ 511 | # LOCK MANAGEMENT 512 | #------------------------------------------------------------------------------ 513 | 514 | #deadlock_timeout = 1s 515 | #max_locks_per_transaction = 64 # min 10 516 | # (change requires restart) 517 | # Note: Each lock table slot uses ~270 bytes of shared memory, and there are 518 | # max_locks_per_transaction * (max_connections + max_prepared_transactions) 519 | # lock table slots. 520 | #max_pred_locks_per_transaction = 64 # min 10 521 | # (change requires restart) 522 | 523 | #------------------------------------------------------------------------------ 524 | # VERSION/PLATFORM COMPATIBILITY 525 | #------------------------------------------------------------------------------ 526 | 527 | # - Previous PostgreSQL Versions - 528 | 529 | #array_nulls = on 530 | #backslash_quote = safe_encoding # on, off, or safe_encoding 531 | #default_with_oids = off 532 | #escape_string_warning = on 533 | #lo_compat_privileges = off 534 | #quote_all_identifiers = off 535 | #sql_inheritance = on 536 | #standard_conforming_strings = on 537 | #synchronize_seqscans = on 538 | 539 | # - Other Platforms and Clients - 540 | 541 | #transform_null_equals = off 542 | 543 | 544 | #------------------------------------------------------------------------------ 545 | # ERROR HANDLING 546 | #------------------------------------------------------------------------------ 547 | 548 | #exit_on_error = off # terminate session on any error? 549 | #restart_after_crash = on # reinitialize after backend crash? 550 | 551 | 552 | #------------------------------------------------------------------------------ 553 | # CUSTOMIZED OPTIONS 554 | #------------------------------------------------------------------------------ 555 | 556 | #custom_variable_classes = '' # list of custom variable class names 557 | -------------------------------------------------------------------------------- /roles/postgres/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for postgres role 3 | -------------------------------------------------------------------------------- /roles/preinstall-gems/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Installing common ruby gems 2 | shell: '/home/{{ deploy_user }}/.rbenv/shims/gem install {{ item }}' 3 | with_items: preinstall_gems 4 | -------------------------------------------------------------------------------- /roles/rbenv-ruby/files/50_rbenv.bash: -------------------------------------------------------------------------------- 1 | export PATH="$HOME/.rbenv/bin:$PATH" 2 | eval "$(rbenv init -)" 3 | -------------------------------------------------------------------------------- /roles/rbenv-ruby/files/bash_50_rbenv: -------------------------------------------------------------------------------- 1 | export PATH="$HOME/.rbenv/bin:$PATH" 2 | eval "$(rbenv init -)" 3 | -------------------------------------------------------------------------------- /roles/rbenv-ruby/files/gemrc: -------------------------------------------------------------------------------- 1 | :sources: 2 | - https://rubygems.org 3 | gem: --no-ri --no-rdoc 4 | -------------------------------------------------------------------------------- /roles/rbenv-ruby/tasks/common-post.yml: -------------------------------------------------------------------------------- 1 | - name: Adds .gemrc 2 | copy: src=gemrc dest=~{{ deploy_user }}/.gemrc owner={{ deploy_user }} group={{ deploy_user }} mode=0750 3 | tags: 4 | - ruby 5 | - ruby:config 6 | - config 7 | 8 | -------------------------------------------------------------------------------- /roles/rbenv-ruby/tasks/main.yml: -------------------------------------------------------------------------------- 1 | # Installing required ruby versions 2 | - include: ruby-install.yml ruby_version={{ version }} 3 | sudo: yes 4 | sudo_user: "{{ deploy_user }}" 5 | 6 | # Common ruby environment tasks (e.g. adding a .gemrc, ...) 7 | - include: common-post.yml 8 | sudo: yes 9 | sudo_user: "{{ deploy_user }}" 10 | 11 | -------------------------------------------------------------------------------- /roles/rbenv-ruby/tasks/ruby-install.yml: -------------------------------------------------------------------------------- 1 | - name: Checks if target ruby is installed 2 | shell: cd; bash -lc "/home/{{ deploy_user }}/.rbenv/bin/rbenv versions | grep {{ version }} | tr '*' ' ' | sed -e 's/\s\+//' | cut -f1 -d' '" 3 | register: ruby_is_installed 4 | tags: 5 | - ruby 6 | - ruby:install 7 | - install 8 | 9 | - name: Installs ruby 10 | shell: cd; bash -lc "MAKEOPTS={{ rbenv_makeopts }} CONFIGURE_OPTS=--disable-install-rdoc /home/{{ deploy_user }}/.rbenv/bin/rbenv install {{ version }}" 11 | when: ruby_is_installed.stdout != version 12 | # Takes no more than 600 secs on a small VM 13 | async: 600 14 | poll: 30 15 | tags: 16 | - ruby 17 | - ruby:install 18 | - install 19 | 20 | - name: Set global ruby version 21 | shell: cd; bash -lc "/home/{{ deploy_user }}/.rbenv/bin/rbenv global {{ ruby_default }}" 22 | tags: 23 | - ruby 24 | - ruby:install 25 | - install 26 | -------------------------------------------------------------------------------- /roles/rbenv/files/50_rbenv.bash: -------------------------------------------------------------------------------- 1 | export PATH="$HOME/.rbenv/bin:$PATH" 2 | eval "$(rbenv init -)" 3 | -------------------------------------------------------------------------------- /roles/rbenv/files/bash_50_rbenv: -------------------------------------------------------------------------------- 1 | export PATH="$HOME/.rbenv/bin:$PATH" 2 | eval "$(rbenv init -)" 3 | -------------------------------------------------------------------------------- /roles/rbenv/files/gemrc: -------------------------------------------------------------------------------- 1 | :sources: 2 | - https://rubygems.org 3 | gem: --no-ri --no-rdoc 4 | -------------------------------------------------------------------------------- /roles/rbenv/handlers/main.yml: -------------------------------------------------------------------------------- 1 | # Configuring ruby environment (e.g. setting the defaut ruby) 2 | - include: rbenv-configure.yml 3 | sudo: yes 4 | sudo_user: "{{ deploy_user }}" 5 | 6 | -------------------------------------------------------------------------------- /roles/rbenv/handlers/rbenv-configure.yml: -------------------------------------------------------------------------------- 1 | - name: Sets global ruby version 2 | shell: cd; bash -lc "/home/{{ deploy_user }}/.rbenv/bin/rbenv global {{ ruby_default }}" 3 | -------------------------------------------------------------------------------- /roles/rbenv/tasks/common-post.yml: -------------------------------------------------------------------------------- 1 | - name: Adds .gemrc 2 | copy: src=gemrc dest=~{{ deploy_user }}/.gemrc owner={{ deploy_user }} group={{ deploy_user }} mode=0750 3 | tags: 4 | - ruby 5 | - ruby:config 6 | - config 7 | 8 | -------------------------------------------------------------------------------- /roles/rbenv/tasks/common-prereqs.yml: -------------------------------------------------------------------------------- 1 | - name: Installs ruby building dependencies 2 | apt: name={{ item }} state=installed update-cache=yes 3 | with_items: ruby_deps 4 | tags: 5 | - ruby 6 | - ruby:install 7 | - install 8 | 9 | 10 | -------------------------------------------------------------------------------- /roles/rbenv/tasks/main.yml: -------------------------------------------------------------------------------- 1 | # Common prereqs (e.g. build tools, libs) 2 | - include: common-prereqs.yml 3 | 4 | # rbenv setup 5 | - include: rbenv-setup.yml 6 | sudo: yes 7 | sudo_user: "{{ deploy_user }}" 8 | 9 | # Common ruby environment tasks (e.g. adding a .gemrc, ...) 10 | - include: common-post.yml 11 | sudo: yes 12 | sudo_user: "{{ deploy_user }}" 13 | 14 | -------------------------------------------------------------------------------- /roles/rbenv/tasks/rbenv-setup.yml: -------------------------------------------------------------------------------- 1 | - name: Installs rbenv 2 | git: repo=https://github.com/sstephenson/rbenv.git dest=~{{ deploy_user }}/.rbenv 3 | tags: 4 | - ruby 5 | - ruby:install 6 | - install 7 | 8 | - name: Ensure fragments bash dir exists 9 | file: dest=~{{ deploy_user }}/.bash.d/ state=directory 10 | 11 | - name: Installs rbenv bash file 12 | copy: src=50_rbenv.bash dest=~{{ deploy_user }}/.bash.d/50_rbenv.bash mode=700 owner={{ deploy_user }} 13 | tags: 14 | - ruby 15 | - ruby:install 16 | - install 17 | 18 | - name: Source rbenv bash file in dot bashrc 19 | lineinfile: dest=~{{ deploy_user }}/.bashrc regexp="rbenv" insertafter=EOF line="source ~/.bash.d/50_rbenv.bash" create=yes 20 | tags: 21 | - ruby 22 | - ruby:install 23 | - install 24 | 25 | - name: Creates plugin directory 26 | file: path=~{{ deploy_user }}/.rbenv/plugins/ owner={{ deploy_user }} group={{ deploy_user }} mode=0755 state=directory 27 | tags: 28 | - ruby 29 | - ruby:install 30 | - install 31 | 32 | - name: Installs ruby-build 33 | git: repo=https://github.com/sstephenson/ruby-build.git dest=~{{ deploy_user }}/.rbenv/plugins/ruby-build 34 | tags: 35 | - ruby 36 | - ruby:install 37 | - install 38 | 39 | -------------------------------------------------------------------------------- /roles/redis/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Add Chris Lea PPA for redis-server 2 | apt_repository: 3 | repo='ppa:chris-lea/redis-server' 4 | state=present 5 | 6 | - name: update apt-cache 7 | action: apt update_cache=yes 8 | 9 | - name: install dependencies 10 | action: apt pkg={{item}} state=installed 11 | with_items: 12 | - redis-server 13 | -------------------------------------------------------------------------------- /roles/ruby-multi/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependencies: 3 | - { role: rbenv-ruby, version: "{{ ruby_versions[0] }}", ruby_default: "{{ ruby_versions[0] }}", when: "0 < ruby_versions|length" } 4 | - { role: rbenv-ruby, version: "{{ ruby_versions[1] }}", when: "1 < ruby_versions|length" } 5 | - { role: rbenv-ruby, version: "{{ ruby_versions[2] }}", when: "2 < ruby_versions|length" } 6 | - { role: rbenv-ruby, version: "{{ ruby_versions[3] }}", when: "3 < ruby_versions|length" } 7 | - { role: rbenv-ruby, version: "{{ ruby_versions[4] }}", when: "4 < ruby_versions|length" } 8 | 9 | -------------------------------------------------------------------------------- /roles/ruby-multi/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - fail: msg= "Sorry - This role only supports up to 5 ruby versions" 2 | when: "5 < ruby_versions|length" 3 | 4 | 5 | -------------------------------------------------------------------------------- /roles/security/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart fail2ban 3 | service: name=fail2ban state=restarted 4 | 5 | - name: reload fail2ban 6 | service: name=fail2ban state=reloaded 7 | -------------------------------------------------------------------------------- /roles/security/tasks/fail2ban.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install fail2ban 3 | apt: pkg=fail2ban state=installed update-cache=yes 4 | register: fail2ban_install 5 | tags: 6 | - apt 7 | 8 | - name: Install ufw-ssh ban actions 9 | template: src=ufw-ssh.conf.j2 dest=/etc/fail2ban/action.d/ufw-ssh.conf 10 | 11 | - name: Install config 12 | template: src=jail.local.j2 dest=/etc/fail2ban/jail.local 13 | notify: 14 | - reload fail2ban 15 | -------------------------------------------------------------------------------- /roles/security/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - include: ufw.yml tags=security 2 | - include: fail2ban.yml tags=security 3 | -------------------------------------------------------------------------------- /roles/security/tasks/ufw.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Allow all outgoing requests 3 | ufw: policy=allow direction=outgoing 4 | 5 | - name: Deny all incoming requests 6 | ufw: policy=deny direction=incoming 7 | 8 | - name: Allow ssh access 9 | ufw: rule=allow name=OpenSSH 10 | 11 | - name: Allow HTTP access 12 | ufw: rule=allow port=80 proto=tcp 13 | 14 | - name: Allow HTTPS access 15 | ufw: rule=allow port=443 proto=tcp 16 | 17 | - name: Start ufw 18 | ufw: state=enabled logging=low 19 | -------------------------------------------------------------------------------- /roles/security/templates/jail.local.j2: -------------------------------------------------------------------------------- 1 | # Fail2Ban configuration file. 2 | # 3 | # This file was composed for Debian systems from the original one 4 | # provided now under /usr/share/doc/fail2ban/examples/jail.conf 5 | # for additional examples. 6 | # 7 | # To avoid merges during upgrades DO NOT MODIFY THIS FILE 8 | # and rather provide your changes in /etc/fail2ban/jail.local 9 | # 10 | # Author: Yaroslav O. Halchenko 11 | # 12 | # $Revision$ 13 | # 14 | 15 | # The DEFAULT allows a global definition of the options. They can be overridden 16 | # in each jail afterwards. 17 | 18 | [DEFAULT] 19 | 20 | # "ignoreip" can be an IP address, a CIDR mask or a DNS host 21 | ignoreip = {{ fail2ban.config.ignoreip }} 22 | bantime = {{ fail2ban.config.bantime }} 23 | maxretry = {{ fail2ban.config.maxretry }} 24 | 25 | # "backend" specifies the backend used to get files modification. Available 26 | # options are "gamin", "polling" and "auto". 27 | # yoh: For some reason Debian shipped python-gamin didn't work as expected 28 | # This issue left ToDo, so polling is default backend for now 29 | backend = auto 30 | 31 | # 32 | # Destination email address used solely for the interpolations in 33 | # jail.{conf,local} configuration files. 34 | destemail = root@localhost 35 | 36 | # 37 | # ACTIONS 38 | # 39 | 40 | # Default banning action (e.g. iptables, iptables-new, 41 | # iptables-multiport, shorewall, etc) It is used to define 42 | # action_* variables. Can be overridden globally or per 43 | # section within jail.local file 44 | banaction = iptables-multiport 45 | 46 | # email action. Since 0.8.1 upstream fail2ban uses sendmail 47 | # MTA for the mailing. Change mta configuration parameter to mail 48 | # if you want to revert to conventional 'mail'. 49 | mta = sendmail 50 | 51 | # Default protocol 52 | protocol = tcp 53 | 54 | # Specify chain where jumps would need to be added in iptables-* actions 55 | chain = INPUT 56 | 57 | # 58 | # Action shortcuts. To be used to define action parameter 59 | 60 | # The simplest action to take: ban only 61 | action_ = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"] 62 | 63 | # ban & send an e-mail with whois report to the destemail. 64 | action_mw = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"] 65 | %(mta)s-whois[name=%(__name__)s, dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s"] 66 | 67 | # ban & send an e-mail with whois report and relevant log lines 68 | # to the destemail. 69 | action_mwl = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"] 70 | %(mta)s-whois-lines[name=%(__name__)s, dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"] 71 | 72 | # Choose default action. To change, just override value of 'action' with the 73 | # interpolation to the chosen action shortcut (e.g. action_mw, action_mwl, etc) in jail.local 74 | # globally (section [DEFAULT]) or per specific section 75 | action = %(action_)s 76 | 77 | # 78 | # JAILS 79 | # 80 | 81 | # Next jails corresponds to the standard configuration in Fail2ban 0.6 which 82 | # was shipped in Debian. Enable any defined here jail by including 83 | # 84 | # [SECTION_NAME] 85 | # enabled = true 86 | 87 | # 88 | # in /etc/fail2ban/jail.local. 89 | # 90 | # Optionally you may override any other parameter (e.g. banaction, 91 | # action, port, logpath, etc) in that section within jail.local 92 | 93 | [ssh] 94 | 95 | enabled = true 96 | port = ssh 97 | banaction = ufw-ssh 98 | filter = sshd 99 | logpath = /var/log/auth.log 100 | maxretry = 6 101 | 102 | [ssh-ddos] 103 | 104 | enabled = false 105 | port = ssh 106 | filter = sshd-ddos 107 | logpath = /var/log/auth.log 108 | maxretry = 3 109 | -------------------------------------------------------------------------------- /roles/security/templates/ufw-ssh.conf.j2: -------------------------------------------------------------------------------- 1 | [Definition] 2 | actionstart = 3 | actionstop = 4 | actioncheck = 5 | actionban = ufw insert 1 deny from to any app OpenSSH 6 | actionunban = ufw delete deny from to any app OpenSSH 7 | -------------------------------------------------------------------------------- /roles/swapfile/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: reload sysctl.conf 3 | command: sysctl -p 4 | -------------------------------------------------------------------------------- /roles/swapfile/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Create swap file 2 | command: fallocate -l {{ swapfile.size_gb }}G {{ swapfile.path }} 3 | when: ansible_swaptotal_mb < 1 4 | 5 | - name: Change swap file permissions 6 | file: path={{ swapfile.path }} owner=root group=root mode=0600 7 | 8 | - name: Make swap file 9 | command: mkswap {{ swapfile.path }} 10 | when: ansible_swaptotal_mb < 1 11 | 12 | - name: Write swap entry in fstab 13 | mount: name=none 14 | src={{ swapfile.path }} 15 | fstype=swap 16 | opts=sw 17 | passno=0 18 | dump=0 19 | state=present 20 | register: swap_fstab 21 | 22 | - name: Mount swap 23 | command: "swapon {{ swapfile.path }}" 24 | when: swap_fstab|changed 25 | 26 | - name: set swapiness 27 | lineinfile: 28 | dest=/etc/sysctl.conf 29 | regexp="^vm.swappiness" 30 | line="vm.swappiness = {{ swapfile.swappiness }}" 31 | state=present 32 | notify: reload sysctl.conf 33 | 34 | - name: set vfs_cache_pressure 35 | lineinfile: 36 | dest=/etc/sysctl.conf 37 | regexp="^vm.vfs_cache_pressure" 38 | line="vm.vfs_cache_pressure = {{ swapfile.vfs_cache_pressure }}" 39 | state=present 40 | notify: reload sysctl.conf 41 | -------------------------------------------------------------------------------- /vars/example.yml: -------------------------------------------------------------------------------- 1 | --- 2 | deploy_user: deploy 3 | # crypted password, generated on a Linux box using: echo 'import crypt,getpass; print crypt.crypt(getpass.getpass(), "$6$AC3bdCF7!")' | python - 4 | deploy_password: '' 5 | deploy_dir: '/var/www/app' # No trailing slash 6 | deploy_hostname: 'myapp.com' 7 | 8 | # SSH key to copy to the server for login 9 | ssh_public_key_files: 10 | - '/Users/me/.ssh/id_rsa.pub' 11 | 12 | # Server Timzone + Locale 13 | timezone: Europe/London 14 | locale: en_GB.UTF-8 15 | 16 | # Swap disk 17 | swapfile: 18 | path: /swapfile 19 | size_gb: 2 20 | swappiness: 10 21 | vfs_cache_pressure: 50 22 | 23 | # Set app specific environment variables 24 | envars: 25 | - 'SECRET_KEY_BASE=please-change-me' 26 | 27 | # Setup postgres database for your app 28 | pg: 29 | db: app_live 30 | user: app_user 31 | pass: app_db_pass 32 | shared_buffers: 128MB 33 | max_connections: 150 34 | listen_addresses: 127.0.0.1 35 | 36 | # Gems you want pre-installed 37 | preinstall_gems: 38 | - bundler 39 | - rails 40 | 41 | # Ruby Settings 42 | rbenv_makeopts: "" # Fill in if needed 43 | 44 | ruby_versions: 45 | - 2.2.2 46 | 47 | ruby_default: "2.2.2" 48 | 49 | # Nginx configuration 50 | nginx: 51 | user: www-data 52 | port: 80 53 | workers: 1 # workers ~= number of cores on machine 54 | worker_connections: 1024 # connections ~= value of ulimit -n 55 | 56 | passenger: 57 | max_pool_size: 2 58 | min_instances: 2 59 | pre_start_url: https://myapp.com 60 | 61 | fail2ban: 62 | config: 63 | ignoreip: 127.0.0.1/8 64 | bantime: 600 65 | maxretry: 10 66 | 67 | # list of crons to add i.e. 68 | # 69 | # - name: Cancel or suspend inactive subscriptions 70 | # minute: 30 71 | # hour: 2 72 | # job: 'cd /var/www/myapp/current && bin/rake daily_subscriptions_updates RAILS_ENV=production' 73 | cron_jobs: [] 74 | 75 | ruby_deps: 76 | - autoconf 77 | - automake 78 | - bison 79 | - build-essential 80 | - curl 81 | - exuberant-ctags 82 | - git-core 83 | - libffi-dev 84 | - libreadline6 85 | - libreadline6-dev 86 | - libreadline-dev 87 | - libsqlite3-0 88 | - libsqlite3-dev 89 | - libssl-dev 90 | - libyaml-dev 91 | - libc6-dev 92 | - libncurses5-dev 93 | - libtool 94 | - libxml2-dev 95 | - libxslt1-dev 96 | - libxrender-dev 97 | - libfontconfig-dev 98 | - openssl 99 | - sqlite3 100 | - subversion 101 | - zlib1g 102 | - zlib1g-dev 103 | - pkg-config 104 | - libpq-dev 105 | - imagemagick 106 | --------------------------------------------------------------------------------