Skip to content

Instantly share code, notes, and snippets.

Last active April 6, 2016 03:26
What would you like to do?
Digital Ocean Discourse production install with LAMP (using Apache)
#create & deploy lamp droplet
#login via ssh as root
#initial setup (ref:
# update /etc/hosts (to "")
nano /etc/hosts
# localhost.localdomain localhost thalassophobia
# set timezone to GMT
dpkg-reconfigure tzdata
#initial security (ref:
# create local admin user "jack"
adduser jack
usermod -a -G sudo jack
# use ssh key/pair authentication
#from local Mac terminal:
scp ~/.ssh/ jack@
# ssh back into digital ocean:
mkdir .ssh
mv .ssh/authorized_keys
chown -R jack:jack .ssh
chmod 700 .ssh
chmod 600 .ssh/authorized_keys
# exit ssh and connect again to verify password is not needed
# disable root login and require ssh key/pair
sudo nano /etc/ssh/sshd_config
PasswordAuthentication no
PermitRootLogin no
sudo restart ssh
# create firewall
sudo nano /etc/iptables.firewall.rules
# Allow all loopback (lo0) traffic and drop all traffic to 127/8 that doesn't use lo0
-A INPUT -i lo -j ACCEPT
# Accept all established inbound connections
# Allow all outbound traffic - you can modify this to only allow certain traffic
# Allow HTTP and HTTPS connections from anywhere (the normal ports for websites and SSL).
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT
# allow ruby thin server connections on port 3000 through 3100
-A INPUT -p tcp --dport 3000:3002 -j ACCEPT
# Allow SSH connections
# The -dport number should be the same port number you set in sshd_config
-A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT
# Allow ping
-A INPUT -p icmp -j ACCEPT
# Log iptables denied calls
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
# Drop all other inbound - default deny unless explicitly allowed policy
# activate firewall settings
sudo iptables-restore < /etc/iptables.firewall.rules
# verify
sudo iptables -L
# activate on startup
sudo nano /etc/network/if-pre-up.d/firewall
/sbin/iptables-restore < /etc/iptables.firewall.rules
sudo chmod +x /etc/network/if-pre-up.d/firewall
# install fail2ban
sudo apt-get install fail2ban
# TODO: IPV6 support (not currently supported by digital ocean)
#install system software
# get everything up-to-date
sudo apt-get update
sudo apt-get upgrade
# don't make any changes to grub menu if prompted
# install git
sudo apt-get install build-essential git libpq-dev
# install postgresql & redis
sudo apt-get install postgresql-9.1 postgresql-contrib-9.1 redis-server
# configure postgresql database
sudo nano /etc/postgresql/9.1/main/pg_hba.conf
host all all trust
host all all ::1/128 trust
sudo /etc/init.d/postgresql restart
# set postgresql root password
passwd postgres
su - postgres
psql -d template1 -c "ALTER USER postgres WITH PASSWORD 'changeme';"
# create discourse database role
createuser discourse --pwprompt
# (make it a superuser when prompted)
su - jack
# install ruby on rails/RVM
sudo apt-get --no-install-recommends install build-essential openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev libgdbm-dev ncurses-dev automake libtool bison subversion pkg-config libffi-dev
\curl -L | bash -s stable --ruby
# activate rvm
source /home/jack/.rvm/scripts/rvm
# or logout and back in
# install ruby gems
rvm rubygems current
# if there's a verification error: rvm rubygems current --verify-downloads 1
gem install thin
# install sendmail
sudo apt-get install sendmail
# install discourse
cd /srv/www/mysite/
# or wherever you want to install it
git clone git://
bundle install
# configure discourse
cp config/database.yml.sample config/database.yml
cp config/redis.yml.sample config/redis.yml
nano config/database.yml
username: discourse
password: discourserolepassword
host: localhost
- ""
# make changes for each instance you want to configure, e.g. production and development
nano config/environments/development.rb
config.action_mailer.delivery_method = :sendmail
config.action_mailer.sendmail_settings = {arguments: '-i'}
rake db:create db:migrate db:seed_fu
redis-cli flushall
# start discourse on port 3000
# best to open a new shell here
cd /srv/www/mysite/discourse
thin start
# and open another shell
cd /srv/www/mysite/discourse
# browse to
# register an account
# activate the account from the email
# stop thin with ctrl-c
# make your user admin
bundle exec rails c
u = User.first
u.admin = true
thin start
# verify you have admin privileges when you log in
# set up production environment
# generate secret token
rake secret
# set secret token
nano config/initializers/secret_token.rb
Discourse::Application.config.secret_token = "1234567etc"
# compile assets
bundle exec rake assets:clean
bundle exec rake assets:precompile
# configure foreman for production environment
# ref:
gem install foreman
nano .env
# start foreman with 3 thin servers
foreman start -c web=3,sidekiq=1,clockwork=1
# make discouse a service, which will run as user jack
# TODO: get it to run as www-data
rvmsudo foreman export upstart /etc/init -a discourse -c web=3,sidekiq=1,clockwork=1 -u jack
# start discourse
sudo start discourse
# enable apache proxy
mkdir /srv/www/mysite/logs
sudo a2enmod rewrite proxy expires headers proxy_balancer proxy_http
sudo nano /etc/apache2/sites-available/mysite
<VirtualHost *:80>
DocumentRoot /srv/www/mysite/discourse/public
CustomLog /srv/www/mysite/logs/access.log common
ErrorLog /srv/www/mysite/logs/error.log
RewriteEngine On
<Proxy balancer://thinservers>
<Proxy *>
Order deny,allow
Allow from all
# Redirect all non-static requests to thin
RewriteRule ^/(.*)$ balancer://thinservers%{REQUEST_URI} [P,QSA,L]
ProxyPreserveHost on
sudo a2ensite mysite
sudo /usr/sbin/apache2ctl configtest
sudo service apache2 restart
# access discourse via http://mysite
sudo stop discourse
git stash
git pull
git stash pop
# resolve conflicts by editing conflicted files if need be
bundle install
rake db:migrate db:seed_fu db:test:prepare
bundle exec rake assets:clean
bundle exec rake assets:precompile
sudo start discourse
Copy link

Updated to use garbage collection as per

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment