├── .gitignore ├── LICENSE ├── README.rst ├── django-stack.yml ├── fabfile.py.template ├── group_vars ├── all ├── dbservers └── webservers ├── hosts ├── requirements.txt ├── roles ├── common │ ├── handlers │ │ └── main.yml │ └── tasks │ │ └── main.yml ├── db │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ ├── config_database.yml │ │ ├── install_db_packages.yml │ │ └── main.yml │ └── templates │ │ ├── pg_hba.conf.j2 │ │ └── postgresql.conf.j2 └── web │ ├── handlers │ └── main.yml │ ├── tasks │ ├── config_supervisor.yml │ ├── config_web_app.yml │ ├── config_web_server.yml │ ├── install_web_packages.yml │ └── main.yml │ └── templates │ ├── nginx_no_ssl.conf.j2 │ ├── nginx_ssl.conf.j2 │ ├── supervisor.conf.j2 │ └── supervisor.newrelic.conf.j2 ├── run_playbook.sh └── ssh_conf └── known_hosts /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | *.egg* 3 | *.swp 4 | .DS_Store 5 | *~ 6 | build 7 | *.db 8 | *.log 9 | _build 10 | fabfile.py 11 | set_envs.sh 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Matthew Makai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Deploy Django with Ansible 2 | ========================== 3 | 4 | This project contains an example Ansible Playbook for deploying a Django 5 | project. You should probably use 6 | `underwear `_ to deploy instead of 7 | these files. These files are intended as examples for the 8 | `January 2014 San Fran Django meetup talk `_. 9 | 10 | 11 | Background 12 | ---------- 13 | I build a lot of Django apps and I wanted a way to easily deploy them to 14 | a traditional virtual private server stack instead of just defaulting to 15 | Heroku. I previously wrote extensive Fabric scripts to automate the 16 | server configuration and deployment process, but that's really the wrong 17 | tool for this goal. Ansible is a much better way to solve the repeatable 18 | deployment problem. 19 | 20 | 21 | First steps 22 | ----------- 23 | Ansible runs over SSH, so we need a way to bootstrap SSH connections through 24 | a non-root user. 25 | 26 | One way to automate these first few steps is with Fabric. The 27 | fabfile.py.template contains one public function, bootstrap_ansible. 28 | bootstrap_ansible calls the other functions to create a non-root user with 29 | sudo privileges, upload public keys for deployment, and lock down root from 30 | logging in. 31 | 32 | Copy fabfile.py.template to fabfile.py, fill in the commented fields at 33 | the top of the script, then run the script with:: 34 | 35 | fab bootstrap_ansible 36 | 37 | Right now the script will prompt you for the password the non-root user should 38 | be created with. I'll automate that manual step away later. 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /django-stack.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This playbook deploys the whole application stack in this site. 3 | 4 | - name: apply common configuration to all nodes 5 | hosts: all 6 | user: "{{ deploy_user }}" 7 | sudo: yes 8 | 9 | roles: 10 | - common 11 | 12 | - name: deploy postgresql and configure the databases 13 | hosts: dbservers 14 | user: "{{ deploy_user }}" 15 | 16 | roles: 17 | - db 18 | 19 | - name: deploy nginx and configure web servers 20 | hosts: webservers 21 | user: "{{ deploy_user }}" 22 | 23 | roles: 24 | - web 25 | -------------------------------------------------------------------------------- /fabfile.py.template: -------------------------------------------------------------------------------- 1 | from os import environ 2 | 3 | from fabric.api import * 4 | from fabric.context_managers import cd 5 | from fabric.contrib.files import sed 6 | 7 | # root user password on new machine before it is locked down 8 | env.password = '' 9 | # all IP address or hostnames of the servers you want to put your SSH keys 10 | # and authorized_host files on, ex: 127.0.0.1 11 | env.hosts = ['',''] 12 | # your full name for the new non-root user 13 | env.new_user_full_name = '' # ex: Matt Makai 14 | # local filesystem directory where your id_rsa, id_rsa.pub, and 15 | # authorized_keys2 files are located (they will be scp'd to target hosts) 16 | # you probably want to create a new deployment pub/private keypair for this 17 | env.ssh_key_dir = '~/.ssh/' # ex: '~/devel/py/deploy-django/ssh_conf/' 18 | 19 | # you shouldn't need to change the below variables 20 | 21 | # run the bootstrap process as root before it is locked down 22 | env.user = 'root' 23 | # username for the new non-root user to be created 24 | env.new_user = 'deployer' # ex: deployer 25 | # group name for the new non-root user to be created 26 | env.new_user_grp = 'deployer' # ex: mygroup 27 | 28 | def bootstrap_ansible(): 29 | local('ssh-keygen -R %s' % env.host_string) 30 | sed('/etc/ssh/sshd_config', '^UsePAM yes', 'UsePAM no') 31 | sed('/etc/ssh/sshd_config', '^#PasswordAuthentication yes', 32 | 'PasswordAuthentication no') 33 | sed('/etc/ssh/sshd_config', '^HostbasedAuthentication no', 34 | 'HostbasedAuthentication yes') 35 | _create_privileged_group() 36 | _create_privileged_user() 37 | _upload_keys(env.new_user) 38 | run('service ssh reload') 39 | 40 | def _create_privileged_group(): 41 | run('/usr/sbin/groupadd ' + env.new_user_grp) 42 | run('mv /etc/sudoers /etc/sudoers-backup') 43 | run('(cat /etc/sudoers-backup ; echo "%' + env.new_user_grp + \ 44 | ' ALL=(ALL) ALL") > /etc/sudoers') 45 | run('chmod 440 /etc/sudoers') 46 | 47 | def _create_privileged_user(): 48 | run('/usr/sbin/useradd -c "%s" -m -g %s %s' % \ 49 | (env.new_user_full_name, env.new_user_grp, env.new_user)) 50 | run('/usr/bin/passwd %s' % env.new_user) 51 | run('/usr/sbin/usermod -a -G ' + env.new_user_grp + ' ' + \ 52 | env.new_user) 53 | run('mkdir /home/%s/.ssh' % env.new_user) 54 | run('chown -R %s /home/%s/.ssh' % (env.new_user, env.new_user)) 55 | run('chgrp -R %s /home/%s/.ssh' % (env.new_user_grp, 56 | env.new_user)) 57 | 58 | def _upload_keys(username): 59 | local('scp ' + env.ssh_key_dir + 'id_rsa ' + env.ssh_key_dir + \ 60 | 'id_rsa.pub ' + env.ssh_key_dir + 'authorized_keys2 ' + \ 61 | username + '@' + env.host_string + ':~/.ssh') 62 | 63 | -------------------------------------------------------------------------------- /group_vars/all: -------------------------------------------------------------------------------- 1 | --- 2 | # Configurable settings for Underwear. Settings that must be changed are 3 | # listed first. Optional ones are listed below in a separate "optional" 4 | # section. 5 | # 6 | ## 7 | 8 | # app_name should be a shorthand, lowercase, no spaces version of the 9 | # application name since this variable will be used as a directory name 10 | app_name: django_app 11 | 12 | # Django project settings directory, which might be different than your 13 | # app name. for example, txt2react 14 | django_settings_dir_name: 15 | 16 | # Django settings module to use when running Manage commands 17 | # for example, txt2react.settings 18 | django_settings: 19 | 20 | # remote git repository to pull application code from, for example: 21 | # ssh://git@github.com/makaimc/txt2react.git 22 | code_repository: 23 | 24 | # name of the user created by the Fabric script that Ansible will use to 25 | # handle server tasks 26 | deploy_user: deployer 27 | 28 | # this will be removed in a future version once I pull it directly 29 | # from ansible's environment variables 30 | webserver_ip: 162.216.19.17 31 | 32 | # server directory for the virtualenv that will be created to run the web app 33 | virtualenv_dir: /home/{{deploy_user}}/venvs/{{app_name}} 34 | 35 | # server directory to put the code into that is pulled from the code 36 | # repository 37 | app_code_dir: /home/{{deploy_user}}/{{app_name}} 38 | 39 | # location of the requirements.txt flie in the code 40 | requirements_file: "{{app_code_dir}}/requirements.txt" 41 | 42 | # location of fixtures to load when configuring the web app 43 | fixtures_file: "{{app_code_dir}}/core/fixtures/prod.json" 44 | 45 | # local directory where public & private deployment ssh keys and 46 | # authorized_keys are located 47 | ssh_dir: /home/{{deploy_user}}/.ssh 48 | 49 | # root domain name the server should run as. do not include a subdomain, 50 | # for example, txt2react.com 51 | root_domain_name: 52 | 53 | # for now, disable SSL (deploying with SSL works but need to update scripts 54 | # to use it) 55 | deploy_ssl: false 56 | 57 | # all environment variables for your app should be included below so 58 | # that Django's manage.py command can run properly 59 | django_env_vars: { 60 | DEBUG: True, 61 | DATABASE_URL: '', # ex: postgres://deployer@127.0.0.1:5432/dbname' 62 | STATIC_URL: '/static/', 63 | SECRET_KEY: '', 64 | } 65 | -------------------------------------------------------------------------------- /group_vars/dbservers: -------------------------------------------------------------------------------- 1 | dbname: t2r 2 | dbuser: deployer 3 | dbpassword: changeme123 4 | -------------------------------------------------------------------------------- /group_vars/webservers: -------------------------------------------------------------------------------- 1 | --- 2 | # variables listed here are applicable to web server group 3 | 4 | httpd_port: 80 5 | 6 | -------------------------------------------------------------------------------- /hosts: -------------------------------------------------------------------------------- 1 | [webservers] 2 | # add a web server IP addresses here, such as 127.0.0.1 3 | 4 | [dbservers] 5 | # add a database IP address here, such as 127.0.0.1 6 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | fabric==1.8.0 2 | ansible==1.4.3 3 | -------------------------------------------------------------------------------- /roles/common/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handler to handle common notifications 3 | -------------------------------------------------------------------------------- /roles/common/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # this playbook contains common plays that will be run on all nodes. 3 | 4 | 5 | - name: install fail2ban for security purposes 6 | apt: pkg=fail2ban state=latest 7 | sudo: yes 8 | -------------------------------------------------------------------------------- /roles/db/handlers/main.yml: -------------------------------------------------------------------------------- 1 | - name: restart postgresql 2 | service: name=postgresql state=restarted 3 | sudo: yes 4 | -------------------------------------------------------------------------------- /roles/db/tasks/config_database.yml: -------------------------------------------------------------------------------- 1 | ### 2 | # configures a new database, new database user, and sets up access privileges 3 | # for an external server to connect 4 | # 5 | --- 6 | 7 | - name: ensure database is created 8 | sudo: yes 9 | sudo_user: postgres 10 | postgresql_db: db={{dbname}} 11 | 12 | - name: ensure user has access to database 13 | sudo: yes 14 | sudo_user: postgres 15 | postgresql_user: db={{dbname}} user={{dbuser}} password={{dbpassword}} priv=ALL 16 | 17 | - name: ensure new user does not have unnecessary privileges 18 | sudo: yes 19 | sudo_user: postgres 20 | postgresql_user: user={{dbuser}} role_attr_flags=NOSUPERUSER 21 | 22 | - name: update postgres access and listen for external hosts 23 | sudo: yes 24 | template: src=pg_hba.conf.j2 dest=/etc/postgresql/9.1/main/pg_hba.conf 25 | 26 | - name: update postgres configuration 27 | sudo: yes 28 | template: src=postgresql.conf.j2 dest=/etc/postgresql/9.1/main/postgresql.conf 29 | notify: 30 | - restart postgresql 31 | -------------------------------------------------------------------------------- /roles/db/tasks/install_db_packages.yml: -------------------------------------------------------------------------------- 1 | ### 2 | # installs packages specific to the database server. this configuration 3 | # runs PostgreSQL 4 | # 5 | --- 6 | 7 | - name: ensure apt cache is up to date 8 | apt: update_cache=yes 9 | sudo: yes 10 | 11 | - name: ensure database packages are installed 12 | apt: name={{item}} 13 | sudo: yes 14 | with_items: 15 | - postgresql 16 | - libpq-dev 17 | - python-psycopg2 18 | 19 | 20 | -------------------------------------------------------------------------------- /roles/db/tasks/main.yml: -------------------------------------------------------------------------------- 1 | ## 2 | # Example Ansible playbook that uses the PostgreSQL module. 3 | # 4 | # This installs PostgreSQL on an Ubuntu system and creates a database 5 | # named according to the variable in the group_vars common file. 6 | # 7 | --- 8 | 9 | - include: install_db_packages.yml 10 | - include: config_database.yml 11 | 12 | -------------------------------------------------------------------------------- /roles/db/templates/pg_hba.conf.j2: -------------------------------------------------------------------------------- 1 | # PostgreSQL Client Authentication Configuration File 2 | # =================================================== 3 | # 4 | # Refer to the "Client Authentication" section in the PostgreSQL 5 | # documentation for a complete description of this file. A short 6 | # synopsis follows. 7 | # 8 | # This file controls: which hosts are allowed to connect, how clients 9 | # are authenticated, which PostgreSQL user names they can use, which 10 | # databases they can access. Records take one of these forms: 11 | # 12 | # local DATABASE USER METHOD [OPTIONS] 13 | # host DATABASE USER ADDRESS METHOD [OPTIONS] 14 | # hostssl DATABASE USER ADDRESS METHOD [OPTIONS] 15 | # hostnossl DATABASE USER ADDRESS METHOD [OPTIONS] 16 | # 17 | # (The uppercase items must be replaced by actual values.) 18 | # 19 | # The first field is the connection type: "local" is a Unix-domain 20 | # socket, "host" is either a plain or SSL-encrypted TCP/IP socket, 21 | # "hostssl" is an SSL-encrypted TCP/IP socket, and "hostnossl" is a 22 | # plain TCP/IP socket. 23 | # 24 | # DATABASE can be "all", "sameuser", "samerole", "replication", a 25 | # database name, or a comma-separated list thereof. The "all" 26 | # keyword does not match "replication". Access to replication 27 | # must be enabled in a separate record (see example below). 28 | # 29 | # USER can be "all", a user name, a group name prefixed with "+", or a 30 | # comma-separated list thereof. In both the DATABASE and USER fields 31 | # you can also write a file name prefixed with "@" to include names 32 | # from a separate file. 33 | # 34 | # ADDRESS specifies the set of hosts the record matches. It can be a 35 | # host name, or it is made up of an IP address and a CIDR mask that is 36 | # an integer (between 0 and 32 (IPv4) or 128 (IPv6) inclusive) that 37 | # specifies the number of significant bits in the mask. A host name 38 | # that starts with a dot (.) matches a suffix of the actual host name. 39 | # Alternatively, you can write an IP address and netmask in separate 40 | # columns to specify the set of hosts. Instead of a CIDR-address, you 41 | # can write "samehost" to match any of the server's own IP addresses, 42 | # or "samenet" to match any address in any subnet that the server is 43 | # directly connected to. 44 | # 45 | # METHOD can be "trust", "reject", "md5", "password", "gss", "sspi", 46 | # "krb5", "ident", "peer", "pam", "ldap", "radius" or "cert". Note that 47 | # "password" sends passwords in clear text; "md5" is preferred since 48 | # it sends encrypted passwords. 49 | # 50 | # OPTIONS are a set of options for the authentication in the format 51 | # NAME=VALUE. The available options depend on the different 52 | # authentication methods -- refer to the "Client Authentication" 53 | # section in the documentation for a list of which options are 54 | # available for which authentication methods. 55 | # 56 | # Database and user names containing spaces, commas, quotes and other 57 | # special characters must be quoted. Quoting one of the keywords 58 | # "all", "sameuser", "samerole" or "replication" makes the name lose 59 | # its special character, and just match a database or username with 60 | # that name. 61 | # 62 | # This file is read on server startup and when the postmaster receives 63 | # a SIGHUP signal. If you edit the file on a running system, you have 64 | # to SIGHUP the postmaster for the changes to take effect. You can 65 | # use "pg_ctl reload" to do that. 66 | 67 | # Put your actual configuration here 68 | # ---------------------------------- 69 | # 70 | # If you want to allow non-local connections, you need to add more 71 | # "host" records. In that case you will also need to make PostgreSQL 72 | # listen on a non-local interface via the listen_addresses 73 | # configuration parameter, or via the -i or -h command line switches. 74 | 75 | 76 | 77 | 78 | # DO NOT DISABLE! 79 | # If you change this first entry you will need to make sure that the 80 | # database superuser can access the database using some other method. 81 | # Noninteractive access to all databases is required during automatic 82 | # maintenance (custom daily cronjobs, replication, and similar tasks). 83 | # 84 | # Database administrative login by Unix domain socket 85 | local all postgres peer 86 | 87 | # TYPE DATABASE USER ADDRESS METHOD 88 | 89 | # "local" is for Unix domain socket connections only 90 | local all all peer 91 | # IPv4 local connections: 92 | host all all 127.0.0.1/32 md5 93 | # IPv6 local connections: 94 | host all all ::1/128 md5 95 | # eventually replace this with dynamic ip address for web 96 | host {{dbname}} {{dbuser}} {{webserver_ip}}/32 trust 97 | # Allow replication connections from localhost, by a user with the 98 | # replication privilege. 99 | #local replication postgres peer 100 | #host replication postgres 127.0.0.1/32 md5 101 | -------------------------------------------------------------------------------- /roles/db/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.1/main' # use data in another directory 42 | # (change requires restart) 43 | hba_file = '/etc/postgresql/9.1/main/pg_hba.conf' # host-based authentication file 44 | # (change requires restart) 45 | ident_file = '/etc/postgresql/9.1/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.1-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 = '*' # 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 = 100 # (change requires restart) 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_directory = '/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 = true # (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 = 24MB # 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 = 3 # in logfile segments, min 1, 16MB each 175 | #checkpoint_timeout = 5min # range 30s-1h 176 | #checkpoint_completion_target = 0.5 # 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/web/handlers/main.yml: -------------------------------------------------------------------------------- 1 | - name: restart nginx 2 | service: name=nginx state=restarted 3 | sudo: yes 4 | 5 | - name: restart supervisor 6 | service: name=supervisor state=restarted 7 | sudo: yes 8 | -------------------------------------------------------------------------------- /roles/web/tasks/config_supervisor.yml: -------------------------------------------------------------------------------- 1 | ### 2 | # sets up the Supervisor configuration and handles any restarting issues 3 | # that can sometimes occur with supervisor.sock 4 | # 5 | --- 6 | 7 | - name: check if supervisor.sock exists 8 | stat: path=/var/run/supervisor.sock 9 | register: supervisor_sock_file 10 | 11 | - name: delete supervisor.sock file so supervisor can start 12 | command: rm /var/run/supervisor.sock 13 | sudo: yes 14 | when: supervisor_sock_file.stat.exists 15 | 16 | - name: write supervisor configuration for web application 17 | template: src=supervisor.conf.j2 dest=/etc/supervisor/conf.d/{{app_name}}.conf 18 | sudo: yes 19 | notify: 20 | - restart supervisor 21 | - restart nginx 22 | 23 | -------------------------------------------------------------------------------- /roles/web/tasks/config_web_app.yml: -------------------------------------------------------------------------------- 1 | ### 2 | # Pulls the code from a remote Git repository, configures the web application, 3 | # creates a virtualenv, and collects static assets 4 | # 5 | --- 6 | 7 | - name: install known_hosts file to access GitHub 8 | copy: src=ssh_conf/known_hosts dest={{ssh_dir}}/known_hosts 9 | 10 | - name: checkout latest web app code 11 | git: repo={{code_repository}} dest={{app_code_dir}} 12 | 13 | - name: check if virtualenv already exists 14 | stat: path={{virtualenv_dir}} 15 | register: venv_dir 16 | 17 | - name: create virtualenv for Django web application 18 | shell: virtualenv {{virtualenv_dir}} 19 | when: venv_dir.stat.isdir is not defined 20 | 21 | - name: install web application dependencies listed in requirements.txt 22 | pip: requirements={{requirements_file}} 23 | virtualenv={{virtualenv_dir}} 24 | 25 | - name: check if app/static directory exists 26 | stat: path={{app_code_dir}}/static 27 | register: static_dir 28 | 29 | - name: create static directory for Django app if it does not exist 30 | command: mkdir {{app_code_dir}}/static 31 | when: static_dir.stat.isdir is not defined 32 | 33 | - name: Django collectstatic 34 | django_manage: command=collectstatic 35 | app_path={{app_code_dir}} 36 | virtualenv={{virtualenv_dir}} 37 | settings={{django_settings}} 38 | environment: django_env_vars 39 | 40 | - name: Django syncdb 41 | django_manage: command=syncdb 42 | app_path={{app_code_dir}} 43 | virtualenv={{virtualenv_dir}} 44 | settings={{django_settings}} 45 | environment: django_env_vars 46 | 47 | 48 | - name: Django migrate 49 | django_manage: command=migrate 50 | app_path={{app_code_dir}} 51 | virtualenv={{virtualenv_dir}} 52 | settings={{django_settings}} 53 | environment: django_env_vars 54 | 55 | - name: Django loaddata 56 | django_manage: command=loaddata 57 | app_path={{app_code_dir}} 58 | virtualenv={{virtualenv_dir}} 59 | fixtures={{fixtures_file}} 60 | settings={{django_settings}} 61 | environment: django_env_vars 62 | -------------------------------------------------------------------------------- /roles/web/tasks/config_web_server.yml: -------------------------------------------------------------------------------- 1 | ### 2 | # Configuration for Nginx web server 3 | # 4 | # The conditionals need to be cleaned up. This is kind of a hack job 5 | # when I split the deployment so you didn't have to use SSL. 6 | --- 7 | 8 | - name: check if nginx ssl directory exists 9 | stat: path=/etc/nginx/{{app_name}} 10 | register: ssldir 11 | 12 | - name: create ssl directory for nginx if it does not already exist 13 | command: mkdir /etc/nginx/{{app_name}} 14 | sudo: yes 15 | when: deploy_ssl and ssldir.stat.isdir is not defined 16 | 17 | - name: write SSL certificate 18 | template: src=ssl_cert/{{app_name}}.sslchain.crt dest=/etc/nginx/{{app_name}}/{{app_name}}.sslchain.crt 19 | sudo: yes 20 | when: deploy_ssl 21 | 22 | - name: write SSL nginx configuration 23 | template: src=nginx_ssl.conf.j2 dest=/etc/nginx/conf.d/{{app_name}}.conf 24 | sudo: yes 25 | when: deploy_ssl 26 | 27 | - name: write non-SSL nginx configuration 28 | template: src=nginx_no_ssl.conf.j2 dest=/etc/nginx/conf.d/{{app_name}}.conf 29 | sudo: yes 30 | when: not deploy_ssl 31 | 32 | - name: write ssl certificate key 33 | template: src=ssl_cert/{{app_name}}.key 34 | dest=/etc/nginx/{{app_name}}/{{app_name}}.key 35 | sudo: yes 36 | when: deploy_ssl 37 | 38 | - name: check if nginx has default symbolic linked website file 39 | stat: path=/etc/nginx/sites-enabled/default 40 | register: nginx_default_file 41 | 42 | - name: delete default symbolic linked website on nginx 43 | command: rm /etc/nginx/sites-enabled/default 44 | sudo: yes 45 | when: nginx_default_file.stat.exists 46 | notify: 47 | - restart nginx 48 | 49 | -------------------------------------------------------------------------------- /roles/web/tasks/install_web_packages.yml: -------------------------------------------------------------------------------- 1 | ### 2 | # updates the APT package cache and install packages necessary for web 3 | # servers 4 | # 5 | --- 6 | 7 | - name: ensure apt cache is up to date 8 | apt: update_cache=yes 9 | sudo: yes 10 | 11 | - name: ensure web server packages are installed 12 | apt: name={{item}} 13 | sudo: yes 14 | with_items: 15 | - git 16 | - python-dev 17 | - libpq-dev 18 | - python-virtualenv 19 | - nginx 20 | - supervisor 21 | - postgresql-client-common 22 | - postgresql-client 23 | 24 | -------------------------------------------------------------------------------- /roles/web/tasks/main.yml: -------------------------------------------------------------------------------- 1 | ## 2 | # these scripts set up a complete web application stack that is connected 3 | # to the remote database backend the other deployment scripts set up 4 | # 5 | --- 6 | 7 | - include: install_web_packages.yml 8 | - include: config_web_server.yml 9 | - include: config_web_app.yml 10 | - include: config_supervisor.yml 11 | 12 | -------------------------------------------------------------------------------- /roles/web/templates/nginx_no_ssl.conf.j2: -------------------------------------------------------------------------------- 1 | upstream app_server_djangoapp { 2 | server localhost:8000 fail_timeout=0; 3 | } 4 | 5 | server { 6 | listen 80; 7 | server_name www.{{root_domain_name}}; 8 | 9 | ssl off; 10 | 11 | access_log /var/log/nginx/{{root_domain_name}}.access.log; 12 | error_log /var/log/nginx/{{root_domain_name}}.error.log info; 13 | 14 | keepalive_timeout 5; 15 | 16 | # nginx should serve up static files and never send to the WSGI server 17 | location /static { 18 | autoindex on; 19 | alias {{app_code_dir}}/assets; 20 | } 21 | 22 | location / { 23 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 24 | proxy_set_header Host $http_host; 25 | proxy_redirect off; 26 | 27 | if (!-f $request_filename) { 28 | proxy_pass http://app_server_djangoapp; 29 | break; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /roles/web/templates/nginx_ssl.conf.j2: -------------------------------------------------------------------------------- 1 | upstream app_server_djangoapp { 2 | server localhost:8000 fail_timeout=0; 3 | } 4 | 5 | server { 6 | server_name {{root_domain_name}}; 7 | rewrite ^(.*) https://www.{{root_domain_name}}$1 permanent; 8 | } 9 | 10 | server { 11 | listen 80; 12 | server_name www.{{root_domain_name}}; 13 | rewrite ^(.*) https://$server_name$1 permanent; 14 | } 15 | 16 | server { 17 | listen 443; 18 | server_name www.{{root_domain_name}}; 19 | 20 | ssl on; 21 | ssl_certificate /etc/nginx/{{app_name}}/{{app_name}}.sslchain.crt; 22 | ssl_certificate_key /etc/nginx/{{app_name}}/{{app_name}}.key; 23 | 24 | # ssl_session_timeout 5m; 25 | 26 | ssl_protocols SSLv2 SSLv3 TLSv1; 27 | ssl_ciphers HIGH:!aNULL:!MD5; 28 | ssl_prefer_server_ciphers on; 29 | 30 | access_log /var/log/nginx/{{root_domain_name}}.access.log; 31 | error_log /var/log/nginx/{{root_domain_name}}.error.log info; 32 | 33 | keepalive_timeout 5; 34 | 35 | # nginx should serve up static files and never send to the WSGI server 36 | location /static { 37 | autoindex on; 38 | alias {{app_code_dir}}/assets; 39 | } 40 | 41 | location / { 42 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 43 | proxy_set_header Host $http_host; 44 | proxy_redirect off; 45 | 46 | if (!-f $request_filename) { 47 | proxy_pass http://app_server_djangoapp; 48 | break; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /roles/web/templates/supervisor.conf.j2: -------------------------------------------------------------------------------- 1 | [program:{{app_name}}] 2 | environment={% for k, v in django_env_vars.iteritems() %}{% if not loop.first %},{% endif %}{{ k }}="{{ v }}"{% endfor %} 3 | 4 | command={{virtualenv_dir}}/bin/gunicorn {{django_settings_dir_name}}.wsgi:application 5 | directory={{app_code_dir}} 6 | user={{deploy_user}} 7 | autostart=true 8 | autorestart=true 9 | redirect_stderr=True 10 | -------------------------------------------------------------------------------- /roles/web/templates/supervisor.newrelic.conf.j2: -------------------------------------------------------------------------------- 1 | [program:{{app_name}}] 2 | environment={% for k, v in django_env_vars.iteritems() %}{% if not loop.first %},{% endif %}{{ k }}="{{ v }}"{% endfor %} 3 | 4 | command={{virtualenv_dir}}/bin/newrelic-admin run-program {{virtualenv_dir}}/bin/gunicorn {{django_settings_dir_name}}.wsgi:application 5 | directory={{app_code_dir}} 6 | user={{deploy_user}} 7 | autostart=true 8 | autorestart=true 9 | redirect_stderr=True 10 | -------------------------------------------------------------------------------- /run_playbook.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ansible-playbook django-stack.yml -K -u deployer 3 | -------------------------------------------------------------------------------- /ssh_conf/known_hosts: -------------------------------------------------------------------------------- 1 | |1|JJyZmR/wf9GBZcvr/GOV8OtyOLQ=|fjnyrwFM/CyO1SEH6o+lnrz55Mk= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== 2 | --------------------------------------------------------------------------------