One place for hosting & domains

      How To Move an Nginx Web Root to a New Location on Ubuntu 20.04


      Introduction

      On Ubuntu, the Nginx web server stores its documents in /var/www/html, which is typically located on the root filesystem with the rest of the operating system. Sometimes, though, it’s helpful to move the document root to another location, such as a separate mounted filesystem. For example, if you serve multiple websites from the same Nginx instance, putting each site’s document root on its own volume allows you to scale in response to the needs of a specific site or client.

      In this guide, you will move an Nginx document root to a new location.

      Prerequisites

      To complete this guide, you will need:

      We will use the domain name your_domain in this tutorial, but you should substitute this with your own domain name.

      • A new location for your document root. In this tutorial, we will use the /mnt/volume-nyc3-01 directory for our new location. If you are using Block Storage on DigitalOcean, this guide will show you how to create and attach your volume. Your new document root location is configurable based on your needs, however. If you are moving your document root to a different storage device, you will want to select a location under the device’s mount point.

      Step 1 — Copying Files to the New Location

      On a fresh installation of Nginx, the document root is located at /var/www/html. By following the prerequisite guides, however, you created a new document root, /var/www/your_domain/html. You may have additional document roots as well. In this step, we will establish the location of our document roots and copy the relevant files to their new location.

      You can search for the location of your document roots using grep. Let’s search in the /etc/nginx/sites-enabled directory to limit our focus to active sites. The -R flag ensures that grep will print both the line with the root directive and the full filename in its output:

      • grep -R "root" /etc/nginx/sites-enabled

      If you followed the prerequisite tutorials on a fresh server, the result will look like this:

      Output

      /etc/nginx/sites-enabled/your_domain: root /var/www/your_domain/html; /etc/nginx/sites-enabled/default: root /var/www/html; /etc/nginx/sites-enabled/default: # deny access to .htaccess files, if Apache's document root /etc/nginx/sites-enabled/default:# root /var/www/your_domain;

      If you have pre-existing setups, your results may differ from what’s shown here. In either case, you can use the feedback from grep to make sure you’re moving the desired files and updating the appropriate configuration files.

      Now that you’ve confirmed the location of your document root, you can copy the files to their new location with rsync. Using the -a flag preserves the permissions and other directory properties, while -v provides verbose output so you can follow the progress of the sync:

      Note: Be sure there is no trailing slash on the directory, which may be added if you use tab completion. When there’s a trailing slash, rsync will dump the contents of the directory into the mount point instead of transferring it into a containing html directory.

      • sudo rsync -av /var/www/your_domain/html /mnt/volume-nyc3-01

      You will see output like the following:

      Output

      sending incremental file list created directory /mnt/volume-nyc3-01 html/ html/index.html sent 318 bytes received 39 bytes 714.00 bytes/sec total size is 176 speedup is 0.49

      With our files in place, let’s move on to modifying our Nginx configuration to reflect these changes.

      Step 2 — Updating the Configuration Files

      Nginx makes use of both global and site-specific configuration files. For background about the hierarchy of configuration files, take a look at “How To Configure The Nginx Web Server On a Virtual Private Server”. We will modify the server block file for our your_domain project: /etc/nginx/sites-enabled/your_domain.

      Note: Remember to replace your_domain with your domain name, and remember that you will be modifying the server block files that were output when you ran the grep command in Step 1.

      Start by opening /etc/nginx/sites-enabled/your_domain in an editor:

      • sudo nano /etc/nginx/sites-enabled/your_domain

      Find the line that begins with root and update it with the new root location. In our case this will be /mnt/volume-nyc3-01/html:

      /etc/nginx/sites-enabled/your_domain

      server {
      
              root /mnt/volume-nyc3-01/html;
              index index.html index.htm index.nginx-debian.html;
              . . .
      }
      . . .
      

      Keep an eye out for any other places that you see the original document root path outputted by grep in Step 1, including in aliases or rewrites. You will also want to update these to reflect the new document root location.

      When you’ve made all of the necessary changes, save and close the file.

      Step 3 — Restarting Nginx

      Once you’ve finished making the configuration changes, you can restart Nginx and test the results.

      First, make sure the syntax is correct:

      If everything is in order, it should return:

      Output

      nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful

      If the test fails, track down and fix the problems.

      Once the test passes, restart Nginx:

      • sudo systemctl restart nginx

      When the server has restarted, visit your affected sites and ensure they’re working as expected. Once you’re comfortable that everything is in order, don’t forget to remove the original copy of the data:

      • sudo rm -Rf /var/www/your_domain/html

      You have now successfully moved your Nginx document root to a new location.

      Conclusion

      In this tutorial, we covered how to change the Nginx document root to a new location. This can help you with basic web server administration, like effectively managing multiple sites on a single server. It also allows you to take advantage of alternative storage devices such as network block storage, which can be helpful in scaling a web site as its needs change.

      If you’re managing a busy or growing web site, you might be interested in learning how to set up Nginx with HTTP/2 to take advantage of its high transfer speed for content.



      Source link

      How To Use Web Forms in a Flask Application


      The author selected the Free and Open Source Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      Web forms, such as text fields and text areas, give users the ability to send data to your application to use it to perform an action, or to send larger areas of text to the application. For example, in a social media application, you might give users a box where they can add new content to their pages. Another example is a login page, where you would give the user a text field to enter their username and a password field to enter their password. The server (your Flask application in this case) uses the data the user submits and either signs them in if the data is valid, or responds with a message like Invalid credentials! to inform the user that the data they submitted is not correct.

      Flask is a lightweight Python web framework that provides useful tools and features for creating web applications in the Python Language. In this tutorial, you’ll build a small web application that demonstrates how to use web forms. The application will have a page for displaying messages that are stored in a Python list, and a page for adding new messages. You’ll also use message flashing to inform users of an error when they submit invalid data.

      Prerequisites

      Step 1 — Displaying Messages

      In this step, you’ll create a Flask application with an index page for displaying messages that are stored in a list of Python dictionaries.

      First open a new file called app.py for editing:

      Add the following code inside the app.py file to create a Flask server with a single route:

      flask_app/app.py

      from flask import Flask, render_template
      
      app = Flask(__name__)
      
      messages = [{'title': 'Message One',
                   'content': 'Message One Content'},
                  {'title': 'Message Two',
                   'content': 'Message Two Content'}
                  ]
      
      @app.route('/')
      def index():
          return render_template('index.html', messages=messages)
      

      Save and close the file.

      In this file, you first import the Flask class and the render_template() function from the flask package. You then use the Flask class to create a new application instance called app, passing the special __name__ variable, which is needed for Flask to set up some paths behind the scenes. Rendering templates is covered in the tutorial How To Use Templates in a Flask Application.

      You then create a global Python list called messages, which has Python dictionaries inside it. Each dictionary has two keys: title for the title of the message, and content for the message content. This is a simplified example of a data storage method; in a real-world scenario, you’d use a database that permanently saves the data and allows you to manipulate it more efficiently.

      After creating the Python list, you use the @app.route() decorator to create a view function called index(). In it, you return a call to the render_template() function, which indicates to Flask that the route should display an HTML template. You name this template index.html (you’ll create it later), and you pass a variable called messages to it. This variable holds the messages list you previously declared as a value and makes it available to the HTML template. View functions are covered in the tutorial How To Create Your First Web Application Using Flask and Python 3.

      Next, create a templates folder in your flask_app directory where Flask searches for templates, then open a template file called base.html, which will have code that other templates will inherit to avoid code repetition:

      • mkdir templates
      • nano templates/base.html

      Add the following code inside the base.html file to create the base template with a navbar and a content block:

      flask_app/templates/base.html

      
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>{% block title %} {% endblock %} - FlaskApp</title>
          <style>
              .message {
                  padding: 10px;
                  margin: 5px;
                  background-color: #f3f3f3
              }
              nav a {
                  color: #d64161;
                  font-size: 3em;
                  margin-left: 50px;
                  text-decoration: none;
              }
      
          </style>
      </head>
      <body>
          <nav>
              <a href="https://www.digitalocean.com/community/tutorials/{{ url_for('index') }}">FlaskApp</a>
              <a href="#">About</a>
          </nav>
          <hr>
          <div class="content">
              {% block content %} {% endblock %}
          </div>
      </body>
      </html>
      

      Save and close the file.

      This base template has all the HTML boilerplate you’ll need to reuse in your other templates. The title block will be replaced to set a title for each page, and the content block will be replaced with the content of each page. The navigation bar has two links, one for the index page where you use the url_for() helper function to link to the index() view function, and the other for an About page if you choose to include one in your application.

      Next, open a template called index.html. This is the template you referenced in the app.py file:

      • nano templates/index.html

      Add the following code to it:

      flask_app/templates/index.html

      {% extends 'base.html' %}
      
      {% block content %}
          <h1>{% block title %} Messages {% endblock %}</h1>
          {% for message in messages %}
              <div class="message">
                  <h3>{{ message['title'] }}</h3>
                  <p>{{ message['content'] }}</p>
              </div>
          {% endfor %}
      {% endblock %}
      

      Save and close the file.

      In this code, you extend the base.html template and replace the contents of the content block. You use an <h1> heading that also serves as a title.

      You use a Jinja for loop in the line {% for message in messages %} to go through each message in the messages list. You use a <div> tag to contain the message’s title and content. You display the title in an <h3> heading and the content in a <p> tag.

      While in your flask_app directory with your virtual environment activated, tell Flask about the application (app.py in this case) using the FLASK_APP environment variable:

      Then set the FLASK_ENV environment variable to development to run the application in development mode and get access to the debugger. For more information about the Flask debugger, see How To Handle Errors in a Flask Application. Use the following commands to do this (on Windows, use set instead of export):

      • export FLASK_ENV=development

      Next, run the application:

      With the development server running, visit the following URL using your browser:

      http://127.0.0.1:5000/
      

      You’ll see the messages in the messages list displayed on the index page:

      Index Page

      Now that you have set up your web application and displayed the messages, you’ll need a way to allow users to add new messages to the index page. This is done through web forms, which you’ll set up in the next step.

      Step 2 — Setting Up Forms

      In this step, you will create a page in your application that allows users to add new messages into the list of messages via a web form.

      Leave the development server running and open a new terminal window.

      First, open your app.py file:

      Add the following route to the end of the file:

      flask_app/app.py

      # ...
      
      @app.route('/create/', methods=('GET', 'POST'))
      def create():
          return render_template('create.html')
      

      Save and close the file.

      This /create route has the methods parameter with the tuple ('GET', 'POST') to accept both GET and POST requests. GET and POST are HTTP methods. By default, only GET requests are accepted, which are used to retrieve data, such as asking the server for an index page or an About page. POST requests are used to submit data to a specific route, which often changes the data on the server.

      In this example, you will ask for the create page using a GET request. The Create page will have a web form with input fields and a Submit button. When a user fills in the web form and clicks the Submit button, a POST request gets sent to the /create route. There you handle the request, validate the submitted data to ensure the user has not submitted an empty form, and add it to the messages list.

      The create() view function currently does only one thing: render a template called create.html when it receives a regular GET request. You will now create this template, then edit the function to handle POST requests in the next step.

      Open a new template file called create.html:

      • nano templates/create.html

      Add the following code to it:

      flask_app/templates/create.html

      {% extends 'base.html' %}
      
      {% block content %}
          <h1>{% block title %} Add a New Message {% endblock %}</h1>
          <form method="post">
              <label for="title">Title</label>
              <br>
              <input type="text" name="title"
                     placeholder="Message title"
                     value="https://www.digitalocean.com/community/tutorials/{{ request.form['title'] }}"></input>
              <br>
      
              <label for="content">Message Content</label>
              <br>
              <textarea name="content"
                        placeholder="Message content"
                        rows="15"
                        cols="60"
                        >{{ request.form['content'] }}</textarea>
              <br>
              <button type="submit">Submit</button>
          </form>
      {% endblock %}
      
      

      Save and close the file.

      In this code, you extend the base.html template and replace the content block with an <h1> heading that serves as a title for the page. In the <form> tag, you set the method attribute to post so the form data gets sent to the server as a POST request.

      In the form, you have a text input field named title; this is the name you’ll use on the application to access the title form data. You give the <input> tag a value of {{ request.form['title'] }}. This is useful to restore the data the user enters so it does not get lost when things go wrong. For example, if the user forgets to fill in the required content text area, a request gets sent to the server and an error message will come back as a response, but the data in the title will not be lost because it will be saved on the request global object, and can be accessed via request.form['title'].

      After the title input field, you add a text area named content with the value {{ request.form['content'] }} for the same reasons mentioned previously.

      Last, you have a Submit button at the end of the form.

      Now, with the development server running, use your browser to navigate to the /create route:

      http://127.0.0.1:5000/create
      

      You will see an “Add a New Message” page with an input field for a message title, a text area for the message’s content, and a Submit button.

      Add a new message

      This form submits a POST request to your create() view function. However, there is no code to handle a POST request in the function yet, so nothing happens after filling in the form and submitting it. In the next step, you’ll handle the incoming POST request when a form is submitted. You’ll check whether the submitted data is valid (not empty), and add the message title and content to the messages list.

      Step 3 — Handling Form Requests

      In this step, you will handle form requests on the application side. You’ll access the form data the user submits via the form you created in the previous step and add it to the list of messages. You’ll also use message flashing to inform users when they submit invalid data. The flash message will only be shown once and will disappear on the next request (if you navigate to another page for example).

      Open the app.py file for editing:

      First, you’ll import the following from the Flask framework:

      • The global request object to access incoming request data that will be submitted via the HTML form you built in the last step.
      • The url_for() function to generate URLs.
      • The flash() function to flash a message when a request is processed (to inform the user that everything went well, or to inform them of an issue if the submitted data is not valid).
      • The redirect() function to redirect the client to a different location.

      Add these imports to the first line in the file:

      flask_app/app.py

      from flask import Flask, render_template, request, url_for, flash, redirect
      
      # ...
      

      The flash() function stores flashed messages in the client’s browser session, which requires setting a secret key. This secret key is used to secure sessions, which allow Flask to remember information from one request to another, such as moving from the new message page to the index page. The user can access the information stored in the session, but cannot modify it unless they have the secret key, so you must never allow anyone to access your secret key. See the Flask documentation for sessions for more information.

      The secret key should be a long random string. You can generate a secret key using the os module with the os.urandom() method, which returns a string of random bytes suitable for cryptographic use. To get a random string using it, open a new terminal and open the Python interactive shell using the following command:

      In the Python interactive shell, import the os module from the standard library and call the os.urandom() method as follows:

      • import os
      • os.urandom(24).hex()

      You’ll get a string similar to the following:

      Output

      'df0331cefc6c2b9a5d0208a726a5d1c0fd37324feba25506'

      You can use the string you get as your secret key.

      To set the secret key, add a SECRET_KEY configuration to your application via the app.config object. Add it directly following the app definition before defining the messages variable:

      flask_app/app.py

      
      # ...
      app = Flask(__name__)
      app.config['SECRET_KEY'] = 'your secret key'
      
      
      messages = [{'title': 'Message One',
                   'content': 'Message One Content'},
                  {'title': 'Message Two',
                   'content': 'Message Two Content'}
                  ]
      # ...
      

      Next, modify the create() view function to look exactly as follows:

      flask_app/app.py

      # ...
      
      @app.route('/create/', methods=('GET', 'POST'))
      def create():
          if request.method == 'POST':
              title = request.form['title']
              content = request.form['content']
      
              if not title:
                  flash('Title is required!')
              elif not content:
                  flash('Content is required!')
              else:
                  messages.append({'title': title, 'content': content})
                  return redirect(url_for('index'))
      
          return render_template('create.html')
      

      In the if statement you ensure that the code following it is only executed when the request is a POST request via the comparison request.method == 'POST'.

      You then extract the submitted title and content from the request.form object that gives you access to the form data in the request. If the title is not provided, the condition if not title would be fulfilled. In that case, you display a message to the user informing them that the title is required using the flash() function. This adds the message to a flashed messages list. You will later display these messages on the page as part of the base.html template. Similarly, if the content is not provided, the condition elif not content will be fulfilled. If so, you add the 'Content is required!' message to the list of flashed messages.

      If the title and the content of the message are properly submitted, you use the line messages.append({'title': title, 'content': content}) to add a new dictionary to the messages list, with the title and content the user provided. Then you use the redirect() function to redirect users to the index page. You use the url_for() function to link to the index page.

      Save and close the file.

      Now, navigate to the /create route using your web browser:

      http://127.0.0.1:5000/create
      

      Fill in the form with a title of your choice and some content. Once you submit the form, you will see the new message listed on the index page.

      Lastly, you’ll display flashed messages and add a link for the “New Message” page to the navigation bar in the base.html template to have easy access to this new page. Open the base template file:

      Edit the file by adding a new <a> tag after the FlaskApp link in the navigation bar inside the <nav> tag. Then add a new for loop directly above the content block to display the flashed messages below the navigation bar. These messages are available in the special get_flashed_messages() function Flask provides. Then add a class attribute called alert to each message and give it some CSS properties inside the <style> tag:

      flask_app/templates/base.html

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>{% block title %} {% endblock %} - FlaskApp</title>
          <style>
              .message {
                  padding: 10px;
                  margin: 5px;
                  background-color: #f3f3f3
              }
              nav a {
                  color: #d64161;
                  font-size: 3em;
                  margin-left: 50px;
                  text-decoration: none;
              }
      
              .alert {
                  padding: 20px;
                  margin: 5px;
                  color: #970020;
                  background-color: #ffd5de;
              }
      
          </style>
      </head>
      <body>
          <nav>
              <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("index') }}">FlaskApp</a>
              <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("create') }}">Create</a>
              <a href="#">About</a>
          </nav>
          <hr>
          <div class="content">
              {% for message in get_flashed_messages() %}
                  <div class="alert">{{ message }}</div>
              {% endfor %}
              {% block content %} {% endblock %}
          </div>
      </body>
      </html>
      

      Save and close the file, and then reload https://127.0.0.1:5000 in your browser. The navigation bar will now have a “Create” item that links to the /create route.

      To see how flash messages work, go to the “Create” page, and click the Submit button without filling the two fields. You’ll receive a message that looks like this:

      No title no content flash message

      Go back to the index page and you’ll see that the flashed messages below the navigation bar disappear, even though they are displayed as part of the base template. If they weren’t flashed messages, they would be displayed on the index page too, because it also inherits from the base template.

      Try submitting the form with a title but no content. You’ll see the message “Content is required!”. Click the FlaskApp link in the navigation bar to go back to the index page, then click the Back button to come back to the Create page. You’ll see that the message content is still there. This only works if you click the Back button, because it saves the previous request. Clicking the Create link in the navigation bar will send a new request, which clears the form, and as a result, the flashed message will disappear.

      You now know how to receive user input, how to validate it, and how to add it to a data source.

      Note:
      The messages you add to the messages list will disappear whenever the server is stopped, because Python lists are only saved in memory, to save your messages permanently, you will need to use a database like SQLite. Check out How To Use the sqlite3 Module in Python 3 to learn how to use SQLite with Python.

      Conclusion

      You created a Flask application where users can add messages to a list of messages displayed on the index page. You created a web form, handled the data the user submits via the form, and added it to your messages list. You also used flash messages to inform the user when they submit invalid data.

      If you would like to read more about Flask, check out the other tutorials in the Flask series.



      Source link

      How To Install Umami Web Analytics Software on Ubuntu 20.04


      Introduction

      Umami is an open-source, self-hosted web analytics application written in Node.js. It focuses on being simple, well-designed, fast, and privacy-focused. It can store data about your website’s visitors in either a MySQL or PostgreSQL database.

      In this tutorial you will install Umami and a PostgreSQL database using Docker Compose, then install Nginx to act as a reverse proxy for Umami. Finally, you will enable secure HTTPS connections by using Certbot to download and configure SSL certificates from the Let’s Encrypt Certificate Authority.

      Prerequisites

      In order to complete this tutorial, you’ll first need the following:

      Note: These prerequisite steps can be skipped if you’re using DigitalOcean’s 1-Click Docker Image. This image will have Docker, Docker Compose, and UFW already installed and configured.

      Launch a new Docker image in the region of your choice, then log in as the root user and proceed with the tutorial. Optionally, you could leave off the sudo parts of all commands, but it’s not necessary.

      Finally, to enable SSL you’ll need a domain name pointed at your server’s public IP address. This should be something like example.com or umami.example.com, for instance. If you’re using DigitalOcean, please see our DNS Quickstart for information on creating domain resources in our control panel.

      When you’ve satisfied all the prerequisites, proceed to Step 1, where you’ll download and launch the Umami software.

      Step 1 — Installing Umami and PostgreSQL with Docker Compose

      Your first step will be to clone the Umami Git repository, update the docker-compose.yml configuration file, and then start the Umami and PostgreSQL containers.

      You’ll download the repo into the /opt directory. Use the cd command to go there now:

      Then use the git command to clone the repo from GitHub:

      • sudo git clone https://github.com/mikecao/umami.git

      This will pull all the software and configuration files into /opt/umami. Move into the newly created umami directory now:

      Now you need to update the project’s docker-compose.yml file. This file is what the docker-compose command uses to configure and launch multiple Docker containers at once. We need to change two options in this file: the IP that Umami binds to, and a random hash used as a salt when encrypting things in the database.

      Before you open docker-compose.yml to edit it, let’s generate a new random hash to paste into the file:

      Output

      tCgKyCWc/3C9VH+Ex0TysXsGEKQklQXm0H3nSnlR48g=

      This uses the openssl command to generate 32 random characters. Copy the output to your clipboard, then open the configuration file:

      • sudo nano docker-compose.yml

      Find the HASH_SALT option, delete the placeholder text, and paste in the random hash you just generated:

      docker-compose.yml

      . . .
            HASH_SALT: replace-me-with-a-random-string
      . . .
      

      Next, find the ports: portion of the configuration:

      docker-compose.yml

      . . .
          ports:
            - "127.0.0.1:3000:3000"
      . . .
      

      Update the "3000:3000" value by prepending 127.0.0.1: to it. This ensures that Umami is only listening on the localhost interface, and is not publicly available. Even though you have a UFW firewall set up, due to some quirks in how Docker networking works, if you didn’t take this step your Umami container would be accessible to the public on port 3000.

      With those configuration changes complete, save the file (CTRL+O then ENTER in nano) and close out of your editor (CTRL+X).

      Now, use docker-compose to start up your two containers:

      • sudo docker-compose up --detach

      The --detach flag tells docker-compose to create the containers in the background, detached from our terminal session:

      Output

      . . . Creating umami_db_1 ... done Creating umami_umami_1 ... done

      Umami and PostgreSQL are now running. You can verify this by using the curl command to fetch the homepage of your new Umami container running on localhost:

      Output

      <!DOCTYPE html><html><head><meta charSet="utf-8"/> . . .

      If a large chunk of HTML is output to your terminal, you know the Umami server is up and running.

      Next, we’ll set up Nginx to reverse proxy Umami from localhost:3000 to the public.

      Step 2 — Installing and Configuring Nginx

      Putting a web server such as Nginx in front of your Node.js server can improve performance by offloading caching, compression, and static file serving to a more efficient process. We’re going to install Nginx and configure it to proxy requests to Umami, meaning it will take care of handing requests from your users to Umami and back again.

      First, refresh your package list, then install Nginx using apt:

      • sudo apt update
      • sudo apt install nginx

      Allow public traffic to ports 80 and 443 (HTTP and HTTPS) using the “Nginx Full” UFW application profile:

      • sudo ufw allow "Nginx Full"

      Output

      Rule added Rule added (v6)

      Next, open up a new Nginx configuration file in the /etc/nginx/sites-available directory. We’ll call ours umami.conf but you could use a different name:

      • sudo nano /etc/nginx/sites-available/umami.conf

      Paste the following into the new configuration file, being sure to replace your_domain_here with the domain that you’ve configured to point to your Umami server. This will be something like umami.example.com, for instance:

      /etc/nginx/sites-available/umami.conf

      server {
          listen       80;
          listen       [::]:80;
          server_name  your_domain_here;
      
          access_log  /var/log/nginx/umami.access.log;
          error_log   /var/log/nginx/umami.error.log;
      
          location / {
            proxy_pass http://localhost:3000;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
      }
      

      This configuration is HTTP-only for now, as we’ll let Certbot take care of configuring SSL in the next step. The rest of the config sets up logging locations and then passes all traffic along to http://localhost:3000, the Umami instance we started up in the previous step.

      Save and close the file, then enable the configuration by linking it into /etc/nginx/sites-enabled/:

      • sudo ln -s /etc/nginx/sites-available/umami.conf /etc/nginx/sites-enabled/

      Use nginx -t to verify that the configuration file syntax is correct:

      Output

      nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful

      And finally, reload the nginx service to pick up the new configuration:

      • sudo systemctl reload nginx

      Your Umami site should now be available on plain HTTP. Load http://your_domain_here and it will look like this:

      A screenshot of the Umami login page, with 'Username' and 'Password' textboxes

      Now that you have your site up and running over HTTP, it’s time to secure the connection with Certbot and Let’s Encrypt certificates.

      Step 3 — Installing Certbot and Setting Up SSL Certificates

      Thanks to Certbot and the Let’s Encrypt free certificate authority, adding SSL encryption to our Umami app will take only two commands.

      First, install Certbot and its Nginx plugin:

      • sudo apt install certbot python3-certbot-nginx

      Next, run certbot in --nginx mode, and specify the same domain you used in the Nginx server_name config:

      • sudo certbot --nginx -d your_domain_here

      You’ll be prompted to agree to the Let’s Encrypt terms of service, and to enter an email address.

      Afterwards, you’ll be asked if you want to redirect all HTTP traffic to HTTPS. It’s up to you, but this is generally recommended and safe to do.

      After that, Let’s Encrypt will confirm your request and Certbot will download your certificate:

      Output

      Congratulations! You have successfully enabled https://umami.example.com You should test your configuration at: https://www.ssllabs.com/ssltest/analyze.html?d=umami.example.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/umami.example.com/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/umami.example.com/privkey.pem Your cert will expire on 2021-12-06. To obtain a new or tweaked version of this certificate in the future, simply run certbot again with the "certonly" option. To non-interactively renew *all* of your certificates, run "certbot renew" - Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal. - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le

      Certbot will automatically reload Nginx to pick up the new configuration and certificates. Reload your site and it should switch you over to HTTPS automatically if you chose the redirect option.

      Your site is now secure and it’s safe to log in with the default user admin and password umami. Please do this immediately, and follow the official first login documentation to get logged in and change your admin password to something more secure.

      When you first log in, you’ll see a somewhat bare dashboard:

      A screenshot of the Umami dashboard after login, showing a

      You have successfully installed and secured your Umami analytics software. In the conclusion of this tutorial you’ll find links to documentation that will get you started with adding your site to Umami, and adding Umami tracking snippet to your site.

      Conclusion

      In this tutorial, you launched the Umami app and a PostgreSQL database using Docker Compose, then set up an Nginx reverse proxy and secured it using Let’s Encrypt SSL certificates.

      Also, you should have logged in and updated the default password already. If not, do that now.

      Afterwards, continue with the official documentation to learn how to add a website to Umami, then start collecting data by installing the tracking code on your website.



      Source link