Flask App Server Setup

This is a detailed log of the steps I went through to configure a Digital Ocean Droplet to run my Flask app.

Creating the Droplet

From the Digital Ocean control panel, choose to create a new Droplet with the latest version of Ubuntu (15.10 as of this writing).

Digital Ocean recommends using the 32-bit version for servers with less than 3 GB of RAM:

A 32-bit operating system is recommended for cloud servers with less than 3 GB of RAM — this is especially true for servers with 1 GB, or less, of RAM. Processes can require significantly more memory on the 64-bit architecture. On servers with a limited amount of RAM, any performance benefits that one might gain from a 64-bit operating system would be diluted by having less memory available for buffers and caching.

Set the rest of the options up as desired. For this server, I chose:

  • The $5/month plan
  • No private networking (this will run on a single server)
  • No Backups (these instructions will get me up and running again)
  • IPv6 (since it’s harder to set it up later)1
  • No User Data (this is helpful for scripting server setup, which I’m not yet doing)

Since I already have SSH keys setup with Digital Ocean, I’ve added them to this new server. If you don’t currently have SSH keys setup, you can follow this guide - and be sure to disable password login!

Initial Configuration

Digital Ocean provides great configuration instructions.

First, create a new user with super user powers:

  • Log in:
    ssh root@droplet_ip_address
  • Create a new user:
    adduser username
  • Give new user sudo privileges:
    gpasswd -a username sudo
  • Disable root login via ssh:
    nano /etc/ssh/sshd_config
    • In that file, replace PermitRootLogin yes with PermitRootLogin no
  • Restart ssh:
    service ssh restart
  • In a new shell, verify the connection works before closing the open connection to root:
    ssh username@SERVER_IP_ADDRESS

Then, copy your ssh public key to the new user account on the server. From my Mac, I ran:
ssh-copy-id username@SERVER_IP_ADDRESS

You can view the installed keys by sshing back into the server and inspecting the ~/.ssh/authorized_keys file. The root user ssh keys live in /root/.ssh/authorized_keys.

Additional Setup

Digital Ocean has also published additional security steps that should be performed on a new Ubuntu server.

First, setup a firewall (additional details):

  • Allow SSH sessions:
    sudo ufw allow ssh
  • Allow web server sessions:
    sudo ufw allow 80/tcp
  • Allow SSL/TLS:
    sudo ufw allow 443/tcp
  • Enable the firewall:
    sudo ufw enable

Next, configure NTP and timezone data to maintain your server’s clock:

  • Configure the time zone by running this command and selecting your country and city:
    sudo dpkg-reconfigure tzdata
  • Install NTP:
    sudo apt-get update sudo apt-get install ntp
  • … that’s it. NTP will be running after the installation.

If they aren’t already configured, set up automatic updates for the server (this was setup by default for me on Ubuntu 15.10):

  • Install the package: sudo apt-get install unattended-upgrades
  • Edit the configuration: nano /etc/apt/apt.conf.d/50unattended-upgrades

    Unattended-Upgrade::Allowed-Origins {
            "${distro_id}:${distro_codename}-security";
    //      "${distro_id}:${distro_codename}-updates";
    //      "${distro_id}:${distro_codename}-proposed";
    //      "${distro_id}:${distro_codename}-backports";
    };
    
  • Enable the updates daily: nano /etc/apt/apt.conf.d/10periodic

    APT::Periodic::Update-Package-Lists "1";
    APT::Periodic::Download-Upgradeable-Packages "1";
    APT::Periodic::AutocleanInterval "7";
    APT::Periodic::Unattended-Upgrade "1";
    

Install Apache (or a complete LAMP stack using these instructions): sudo apt-get install apache2

Setup git:

  • Install git:
    sudo apt-get install git
  • Set user name2:
    git config --global user.name "Your Name"
  • Set user email:
    git config --global user.email "youremail@domain.com"

Flask App

File Structure for App

I’ll go into the details of the Flask app in a later post, but it’s important to understand the directory and file configuration before we get to deploying the app (as many of the commands are specific to this setup). I’ve taken a cue from these two Digital Ocean guides, and structured the app this way:

~/repoName
    |-- appName.conf        # Apache config file, to be copied after deployment
    |-- appName.wsgi        # The wsgi script Apache will call into
    |-- requirements.txt    # Virtual Environment configuration
    |__ /venv               # Virtual Environment (ignored by repo)
    |__ /appName            # Our Application Module
         |-- __init__.py    # The main logic
         |__ /templates
         |__ /static
         |__ ..
         |__ .
    |__ ..
    |__ .

Everything needed on the server is contained here; the steps below walk through cloning the repo, installing the necessary tools, and configuring the server to run the app.

Flask Deployment

At this point you have a basic webserver up and running; navigating to http://SERVER_IP_ADDRESS/ in your browser should take you to the Apache default page.

To deploy my Flask app, I’m using mod_wsgi. First, that needs to be installed:
sudo apt-get install libapache2-mod-wsgi

Install pip for managing Python packages:
sudo apt-get install python-pip

Install Virtualenv for managing the Python dependencies (as on the development machine):
sudo apt-get install python-virtualenv

Move to /var/www/ and clone the app git repo:

  • Move to the directory: cd /var/www/
  • Clone git repo: sudo git clone GIT_REPO_URL
  • For some reason, the repo comes down from BitBucket with lowercase name: sudo mv reponame/ repoName/

Setup the virtual environment:

  • Move to cloned repo: cd repoName
  • Create environment:sudo virtualenv --no-site-packages --distribute venv
  • Install modules: sudo venv/bin/pip install -r requirements.txt

Copy the site configuration for Apache, enable site, and restart Apache:

  • Copy the configuration: sudo cp appName.conf /etc/apache2/sites-available/
  • Enable the site: sudo a2ensite appName
  • Restart Apache: sudo service apache2 restart

HTTPS (added on 2016-03-05)

These instructions (as I discussed in this post) provide great guidance for configuring HTTPS with a certificate from Let’s Encrypt. First, install the Let’s Encrypt tool: sudo git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt

Install a certificate: cd /opt/letsencrypt
./letsencrypt-auto --apache -d example.com

Install the le-renew script for auto-renewal:

sudo curl -L -o /usr/local/sbin/le-renew https://gist.githubusercontent.com/jeffvautin/5d98b4f7d42ab29463e2/raw/6a4b01a4caba2efd1e3dbc97a33d2ef1f80ecf26/le-renew.sh
sudo chmod +x /usr/local/sbin/le-renew

Edit the cron configuration: sudo crontab -e

And add this line, to run the renewal script weekly: 30 2 * * 1 /usr/local/sbin/le-renew example.com >> /var/log/le-renew.log

Other stuff

There’s still a bit to do, but I’ll circle back to these items after I go through the mechanics of the app in my next post:

In the future, I should script some of this3, but getting it written down is a good first step.


  1. I’m not worrying about this at the moment, but Digital Ocean provides instruction for configuring applications to use IPv6

  2. To see these settings on another machine, or to make sure you’ve set them correctly, you can use the command git config --list

  3. This article on Droplet Metadata may be helpful.