Essetee's Website

Deploy Flask + Nginx + MongoDB

Installing the needed packages.

sudo apt update && sudo apt upgrade -y 
sudo apt install -y python3-pip python3-dev build-essential libssl-dev \ 
                    libffi-dev python3-setuptools python3-venv nginx				        

If you are on a Debian server, you need 1 more package:

sudo apt install -y ufw

Let's configure our ufw firewall.

sudo ufw default allow outgoing
sudo ufw default deny incoming
sudo ufw allow ssh
sudo ufw allow http/tcp
sudo ufw enable

Installing MongoDB

I have a separate guide to install MongoDB

You can follow that guide or just download the bash scripts I wrote to auto installing MongoDB.

1. Debian: Download for debian
2. Ubuntu: Download for ubuntu

Download it to your home folder or to your ~/bin folder. Extract it with tar xf installMongoDB* and after extracting the file you can run it with the command ./installMongoDB*.

Starting the MongoDB server: sudo systemctl enable --now mongod

Now we will create our Flask Application with the name myproject.

mkdir ~/myproject
cd ~/myproject

Now create a virtual environment for our project.

python3 -m venv myprojectenv

and activate it:

source myprojectenv/bin/activate

Installing flask and gunicorn who will be serving our python code. Also installing the MongoDB module.

pip install wheel gunicorn flask pymongo

Now create our flask pyhton file: nano ~/myproject/myproject.py and put following code in it:

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "<h1 style='color:navy'>This is my Homepage</h1>"

if __name__ == "__main__":
    app.run(host='0.0.0.0')

Now we can test it. Our application will run on port 5000, so we need to allow that connection in our firewall: sudo ufw allow 5000

Run our application: python myproject.py and to see the result surf to: http://localhost:5000

press ctrl+c to stop the server.

Gunicorn, our python handler. We need to create a file for gunicorn: nano ~/myproject/wsgi.py and paste in the content below.

from myproject import app

if __name__ == "__main__":
    app.run()

Now run the command:

gunicorn --bind 0.0.0.0:5000 wsgi:app

Surf to http://localhost:5000 to see if gunicorn did his job.

Let's leave our virtual environment: deactivate

Making a systemd service for gunicorn: sudo nano /etc/systemd/system/myproject.service and paste the following:

[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

[Service]
User=serge
Group=www-data
WorkingDirectory=/home/serge/myproject
Environment="PATH=/home/serge/myproject/myprojectenv/bin"
ExecStart=/home/serge/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app

[Install]
WantedBy=multi-user.target

Activating the service: sudo systemctl enable --now myproject and check it: sudo systemctl status myproject

Now it's time to configure our nginx server.

sudo nano /etc/nginx/sites-available/myproject and paste following content:

server {
    listen 80;
    server_name YOUR_DOMAIN www.YOUR_DOMAIN;

    location / {
        include proxy_params;
        proxy_pass http://unix:/home/serge/myproject/myproject.sock;
    }
}

For the moment you can put localhost for YOUR_DOMAIN.

Activating our site: sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled

Controlling of the syntax is correct: sudo nginx -t

Restarting the nginx server: sudo systemctl restart nginx

Now we need to delete the port 5000 in our firewall: sudo ufw delete allow 5000

Giving persmisson to nginx in our firewall: sudo ufw allow 'Nginx Full'

Set the correct permissions on our home folder: sudo chmod 755 /home/$USER

The following section is for https access to your site. If you don't have a domain name, don't install it!

sudo apt install python3-certbot-nginx

sudo certbot --nginx -d YOUR_DOMAIN -d www.YOUR_DOMAIN

sudo ufw delete allow 'Nginx HTTP'

https://your_domain

End certbot install

Before we go further, be sure everything is fine. I will show you now how to change the myproject.py file to use with MongoDB.

Open your myproject.py file with your editor of choise and delete everything except the lines:

if __name__ == "__main__":
    app.run(host='0.0.0.0')

If you are familiar with an sql based server (MySQL, PostgreSQL, SQLite3, ...) mongodb take another approch, it's a NoSQL database. You have only KEYS and VALUES. For a database name, MongoDB calls that a collection. A table is a document.

If you want to make a database from your books you need an author and the title of the book. (we keep it simple). I presume you know flask, if not learn flask first. In our file we import the flask modules:

from flask import Flask, render_template, request, redirect, url_for

For the MongoDB server we import the modules:

from pymongo import MongoClient

For the name of the database we choose the name library(collection) and for the tables we choose the name mybooks(document). You don't need to create the database and tables first. Everyting is made when we start the code below. Add to your myproject.py.

client = MongoClient('localhost', 27017)
db = client.library
books = db.mybooks
  1. The MongoDB runs on port 27017.
  2. db = the name of our database
  3. books = the table name inside the db
@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'POST':
        author = request.form['author']
        title = request.form['title']
        books.insert_one({'author': author, 'title': title })
        return redirect(url_for('index')) # we refer here to the function not the index.html!
    all_books = books.find().sort('author')
    return render_template('index.html', books=all_books)

The first line looks strange, but here we defined that we accept POST and GET requests.

In our templates folder we make a basic index.html file with following content:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>MongDB Flask App</title>
    </head>
    <body>
        <h1>MongoDB Flask App</h1>
        <hr>
        <div>
            <form method="POST">
                <p>
                    <b><label for="author">Author of the book</label></b>
                </p>
                <p>
                <input type="text" name="author" placeholder="Author">
                </p>
                <p>
                    <b><label for="title">Title of the book</label></b>
                </p>
                <p>
                    <input type="text" name="title" placeholder="Title">
                    
                </p>
                <button type="submit">Submit</button>
            </form>
            <hr>
            {% for book in books %}
            <div>
            <!--Fetching a book-->
            <p> {{ book['author'] }} <i>
                ({{ book['title'] }})</i> 
            </p>
            </div>
            {% endfor %}
        </div>
    </body>
    </html>

Nothing fancy, but it will do the job. If you fill in the form and you click on submit, the author and the title of your library will show up in your homepage.

Add some more to test out.

Now open your .bashrc or .bash_aliases file. Add the lines alias mongorestore='mongorestore --dir=/home/$USER/myproject/dump --drop'
alias mongodump='mongodump -o ~/home/$USER/myproject/dump'

Exit your shell: exit and reopen a new shell for the changes to take effect.

If you have added some more, in the shell give the command: mongodump. This command will dump a backup of your MongoDB in your ~/myproduct/dump folder.

How do we delete an entry in our database ? Put in the following code:

from bson.objectid import ObjectId

@app.post('/<id>/delete')
def delete(id):
    pages.delete_one({"_id": ObjectId(id)})
    return redirect(url_for('index'))

And we update our index.html code:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>MongDB Flask App</title>
    </head>
    <body>
        <h1>MongoDB Flask App</h1>
        <hr>
        <div>
            <form method="POST">
                <p>
                    <b><label for="author">Author of the book</label></b>
                </p>
                <p>
                <input type="text" name="author" placeholder="Author">
                </p>
                <p>
                    <b><label for="title">Title of the book</label></b>
                </p>
                <p>
                    <input type="text" name="title" placeholder="Title">
                    
                </p>
                <button type="submit">Submit</button>
            </form>
                    <hr>
                    {% for book in books %}
                    <div>
                <!--Fetching a book-->
                    <p> {{ book['author'] }} <i>
                            ({{ book['title'] }})</i> 
                    </p>
                    <!-- Deleting a book -->
                    <form method="POST" 
                              action="{{ url_for('delete', id=book['_id']) }}">
                    <input 
                              type="submit"
                              value="delete book"
                              onclick="return 
                              confirm('Are you sure you want to delete this entry?')"/>
                    </form>
                    </div>
                    {% endfor %}
        </div>
    </body>
    </html>

Try out the code.

That it's. There are many tutorials about MongoDB and Flask. I just let you show here how you can combine everything together.