One place for hosting & domains

      Set Up a Reverse Proxy in an LXD Container to Host Multiple Websites


      Updated by Linode Contributed by Simos Xenitellis

      Introduction

      LXD (pronounced “Lex-Dee”) is a system container manager build on top of Linux Containers (LXC) supported by Canonical. The goal of LXD is to provide an experience similar to a virtual machine but through containerization rather than hardware virtualization. Compared to Docker for delivering applications, LXD offers nearly full operating-system functionality with additional features such as snapshots, live migrations, and storage management.

      A reverse proxy is a server that sits between internal applications and external clients, forwarding client requests to the appropriate server. While many common applications, such as Node.js, are able to function as servers on their own, they may lack a number of advanced load balancing, security, and acceleration features.

      This guide explains the creation of a reverse proxy in an LXD container in order to host multiple websites, each in their own additional containers. You will utilize NGINX and Apache web servers, while also relying on NGINX as a reverse proxy.

      Please refer to the following diagram to understand the reverse proxy created in this guide.

      Diagram of LXD reverse proxy and web servers

      In this guide you will:

      Note

      For simplicity, the term container is used throughout this guide to describe the LXD system containers.

      Before You Begin

      1. Complete A Beginner’s Guide to LXD: Setting Up an Apache Web Server In a Container. The guide instructs you to create a container called web with the Apache web server for testing purposes. Remove this container by running the following commands.

        lxc stop web
        lxc delete web
        

        Note

      2. This guide will use the hostnames apache1.example.com and nginx1.example.com for the two example websites. Replace these names with hostnames you own and setup their DNS entries to point them to the IP address of the server you created. For help with DNS see our DNS Manager Guide.

      Creating the Containers

      1. Create two containers called apache1 and nginx1, one with the Apache web server and another with the NGINX web server, respectively. For any additional websites, you may create new containers with your chosen web server software.

        lxc launch ubuntu:18.04 apache1
        lxc launch ubuntu:18.04 nginx1
        
      2. Create the proxy container for the reverse proxy.

        lxc launch ubuntu:18.04 proxy
        
      3. List the containers with the list command.

        lxc list
        
      4. The output will look similar to the following.

          
        +---------+---------+---------------------+-----------------------------------------------+------------+-----------+
        |  NAME   |  STATE  |        IPV4         |                     IPV6                      |    TYPE    | SNAPSHOTS |
        +---------+---------+---------------------+-----------------------------------------------+------------+-----------+
        | apache1 | RUNNING | 10.10.10.204 (eth0) | fd42:67a4:b462:6ae2:216:3eff:fe01:1a4e (eth0) | PERSISTENT |           |
        +---------+---------+---------------------+-----------------------------------------------+------------+-----------+
        | nginx1  | RUNNING | 10.10.10.251 (eth0) | fd42:67a4:b462:6ae2:216:3eff:febd:67e3 (eth0) | PERSISTENT |           |
        +---------+---------+---------------------+-----------------------------------------------+------------+-----------+
        | proxy   | RUNNING | 10.10.10.28 (eth0)  | fd42:67a4:b462:6ae2:216:3eff:fe00:252e (eth0) | PERSISTENT |           |
        +---------+---------+---------------------+-----------------------------------------------+------------+-----------+
        
        

        There are three containers, all in the RUNNING state – each with their own private IP address. Take note of the IP addresses (both IPv4 and IPv6) for the container proxy. You will need them to configure the proxy container in a later section.

        Now that the containers have been created, the following steps will detail how to set up the web server software in the apache1 and nginx1 containers, and the proxy container so that the web servers are accessible from the internet.

      Configuring the Apache Web Server Container

      When using a reverse proxy in front of a web server, the web server does not know the IP addresses of visitors. The web server only sees the IP address of the reverse proxy. However, each web server has a way to identify the real remote IP address of a visitor. For Apache, this is performed with the Remote IP Apache module. For the module to work, the reverse proxy must be configured to pass the remote IP address’ information.

      1. Start a shell in the apache1 container.

        lxc exec apache1 -- sudo --user ubuntu --login
        
      2. Update the package list in the apache1 container.

        sudo apt update
        
      3. Install the package apache2 in the container.

        sudo apt install -y apache2
        
      4. Create the file /etc/apache2/conf-available/remoteip.conf.

        remoteip.conf
        1
        2
        
        RemoteIPHeader X-Real-IP
        RemoteIPTrustedProxy 10.10.10.28 fd42:67a4:b462:6ae2:216:3eff:fe00:252e

        You can use the nano text editor by running the command sudo nano /etc/apache2/conf-available/remoteip.conf. Note, these are the IP addresses of the proxy container shown earlier, for both IPv4 and IPv6. Replace these with the IPs from your lxc list output.

        Note

        Instead of specifying the IP addresses, you can also use the hostname proxy.lxd. However, the RemoteIP Apache module is peculiar when using the hostname and will use only one of the two IP addresses (either IPv4 or IPv6), which means the Apache web server will not know the real source IP address for some connections. By listing explicitly both IPv4 and IPv6 addresses, you can be certain that RemoteIP will successfully accept the source IP information from all connections of the reverse proxy.

      5. Enable the new remoteip.conf configuration.

        sudo a2enconf remoteip
        
          
        Enabling conf remoteip.
        To activate the new configuration, you need to run:
        systemctl reload apache2
        
        
      6. Enable the remoteip Apache module.

        sudo a2enmod remoteip
        
          
        Enabling module remoteip.
        To activate the new configuration, you need to run:
        systemctl restart apache2
        
        
      7. Edit the default web page for Apache to make a reference that it runs inside a LXD container.

        sudo nano /var/www/html/index.html
        

        Change the line “It works!” (line number 224) to “It works inside a LXD container!” Save and exit.

      8. Restart the Apache web server.

        sudo systemctl reload apache2
        
      9. Exit back to the host.

        exit
        

      You have created and configured the Apache web server, but the server is not yet accessible from the Internet. It will become accessible after you configure the proxy container in a later section.

      Creating the NGINX Web Server Container

      Like Apache, NGINX does not know the IP addresses of visitors when using a reverse proxy in front of a web server. It only sees the IP address of the reverse proxy instead. Each NGINX web server software can identify the real remote IP address of a visitor with the Real IP module. For the module to work, the reverse proxy must be configured accordingly to pass the information regarding the remote IP addresses.

      1. Start a shell in the nginx1 container.

        lxc exec nginx1 -- sudo --user ubuntu --login
        
      2. Update the package list in the nginx1 container.

        sudo apt update
        
      3. Install NGINX in the container.

        sudo apt install -y nginx
        
      4. Create the file /etc/nginx/conf.d/real-ip.conf.

        real-ip.conf
        1
        2
        
        real_ip_header    X-Real-IP;
        set_real_ip_from  proxy.lxd;

        You can use the nano text editor by running the command sudo nano /etc/nginx/conf.d/real-ip.conf.

        Note

        You have specified the hostname of the reverse proxy, proxy.lxd. Each LXD container gets automatically a hostname, which is the name of the container plus the suffix .lxd. By specifying the set_real_ip_from field with proxy.lxd, you are instructing the NGINX web server to accept the real IP address information for each connection, as long as that connection originates from proxy.lxd. The real IP address information will be found in the HTTP header X-Real-IP in each connection.

      5. Edit the default web page for NGINX to make a reference that it runs inside a LXD container.

        sudo nano /var/www/html/index.nginx-debian.html
        

        Change the line “Welcome to nginx!” (line number 14) to “Welcome to nginx running in a LXD system container!”. Save and exit.

      6. Restart the NGINX web server.

        sudo systemctl reload nginx
        
      7. Exit back to the host.

        exit
        

      You have created and configured the NGINX web server, but the server is not accessible yet from the Internet. It will become accessible after you configure the proxy container in the next section.

      Setting up the Reverse Proxy

      In this section you will configure the container proxy. You will install NGINX and set it up as a reverse proxy, then add the appropriate LXD proxy device in order to expose both ports 80 and 443 to the internet.

      1. Add LXD proxy devices to redirect connections from the internet to ports 80 (HTTP) and 443 (HTTPS) on the server to the respective ports at the proxy container.

        lxc config device add proxy myport80 proxy listen=tcp:0.0.0.0:80 connect=tcp:127.0.0.1:80 proxy_protocol=true
        lxc config device add proxy myport443 proxy listen=tcp:0.0.0.0:443 connect=tcp:127.0.0.1:443 proxy_protocol=true
        
          
        Device myport80 added to proxy
        Device myport443 added to proxy
        
        

        The lxc config device add command takes as arguments:

        Argument Explanation
        proxy The name of the container.
        myport80 A name for this proxy device.
        proxy The type of the LXD device (LXD proxy device).
        listen=tcp:0.0.0.0:80 The proxy device will listen on the host (default) on port 80, protocol TCP, on all interfaces.
        connect=tcp:127.0.0.1:80 The proxy device will connect to the container on port 80, protocol TCP, on the loopback interface. In previous versions of LXD you could have specified localhost here. However, in LXD 3.13 or newer, you can only specify IP addresses.
        proxy_protocol=true Request to enable the PROXY protocol so that the reverse proxy will get the originating IP address from the proxy device.

        Note

        If you want to remove a proxy device, use lxc config device remove. If you want to remove the above device myport80, run the following command:

        lxc config device remove proxy myport80
        

        Where proxy is the name of the container, and myport80 is the name of the device.

      2. Start a shell in the proxy container.

        lxc exec proxy -- sudo --user ubuntu --login
        
      3. Update the package list.

        sudo apt update
        
      4. Install NGINX in the container.

        sudo apt install -y nginx
        
      5. Logout from the container.

        logout
        

      Direct Traffic to the Apache Web Server From the Reverse Proxy

      The reverse proxy container is running and the NGINX package has been installed. To work as a reverse proxy, add the appropriate website configuration so that NGINX can identify (with server_name below) the appropriate hostname, and then pass (with proxy_pass below) the connection to the appropriate LXD container.

      1. Start a shell in the proxy container.

        lxc exec proxy -- sudo --user ubuntu --login
        
      2. Create the file apache1.example.com in /etc/nginx/sites-available/ for the configuration of your first website.

        apache1.example.com
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        
        server {
                listen 80 proxy_protocol;
                listen [::]:80 proxy_protocol;
        
                server_name apache1.example.com;
        
                location / {
                        proxy_set_header Host $host;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_pass http://apache1.lxd;
                }
        
                real_ip_header proxy_protocol;
                set_real_ip_from 127.0.0.1;
        }

        You can run sudo nano /etc/nginx/sites-available/apache1.example.com to open up a text editor and add the configuration. Note, in this case you only need to edit the server_name to be the hostname of the website.

      3. Enable the website.

        sudo ln -s /etc/nginx/sites-available/apache1.example.com /etc/nginx/sites-enabled/
        
      4. Restart the NGINX reverse proxy. By restarting the service, NGINX will read and apply the new site instructions just added to /etc/nginx/sites-enabled.

        sudo systemctl reload nginx
        
      5. Exit the proxy container and return back to the host.

        logout
        
      6. From your local computer, visit the URL of your website with your web browser. You should see the default Apache page:

        Web page of Apache server running in a container

        Note

        If you look at the Apache access.log file (default file /var/log/apache2/access.log), it will still show the private IP address of the proxy container instead of the real IP address. This issue is specific to the Apache web server and has to do with how the server prints the logs. Other software on the web server will be able to use the real IP. To fix this through the Apache logs, see the section Troubleshooting.

      Direct Traffic to the NGINX Web Server From the Reverse Proxy

      The reverse proxy container is running and the NGINX package has been installed. To work as a reverse proxy, you will add the appropriate website configuration so NGINX can identify (with server_name below) the appropriate hostname, and then pass (with proxy_pass below) the connection to the appropriate LXD container with the actual web server software.

      1. Start a shell in the proxy container.

        lxc exec proxy -- sudo --user ubuntu --login
        
      2. Create the file nginx1.example.com in /etc/nginx/sites-available/ for the configuration of your second website.

        nginx1.example.com
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        
        server {
                listen 80 proxy_protocol;
                listen [::]:80 proxy_protocol;
        
                server_name nginx1.example.com;
        
                location / {
                        proxy_set_header Host $host;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_pass http://nginx1.lxd;
                }
        
                real_ip_header proxy_protocol;
                set_real_ip_from 127.0.0.1;
        }

        You can run sudo nano /etc/nginx/sites-available/nginx1.example.com to create the configuration. Note, you only need to edit the fields server_name to be the hostname of the website.

      3. Enable the website.

        sudo ln -s /etc/nginx/sites-available/nginx1.example.com /etc/nginx/sites-enabled/
        
      4. Restart the NGINX reverse proxy service.

        sudo systemctl reload nginx
        
      5. Exit the proxy container and return back to the host.

        logout
        
      6. From your local computer, visit the URL of your website with your web browser. You should see the following default NGINX page.

        Web page of the nginx server running in a container

      Adding Support for HTTPS with Let’s Encrypt

      1. Start a shell in the proxy container.

        lxc exec proxy -- sudo --user ubuntu --login
        
      2. Add the repository ppa:certbot/certbot by running the following command.

        sudo add-apt-repository ppa:certbot/certbot
        
      3. Output will look similar to the following.

          
              This is the PPA for packages prepared by Debian Let's Encrypt Team and backported for Ubuntu(s).
              More info: https://launchpad.net/~certbot/+archive/ubuntu/certbot
             Press [ENTER] to continue or Ctrl-c to cancel adding it.
        
             Get:1 http://security.ubuntu.com/ubuntu bionic-security InRelease [88.7 kB]
             ...
             Fetched 3360 kB in 2s (2018 kB/s)
             Reading package lists... Done
        
        
      4. Install the following two packages to a) support the creation of Let’s Encrypt certificates; and b) auto-configure the NGINX reverse proxy to use Let’s Encrypt certificates. The packages are pulled from the newly-created repository.

        sudo apt-get install certbot python-certbot-nginx
        

        Note

        This configures the reverse proxy to also act as a TLS Termination Proxy. Any HTTPS configuration is only found in the proxy container. By doing so, it is not necessary to perform any tasks inside the web server containers relating to certificates and Let’s Encrypt.

      5. Run certbot as root with the --nginx parameter in order to perform the auto-configuration of Let’s Encrypt for the first website. You will be asked to supply a valid email address for urgent renewal and security notices. You will then be asked to accept the Terms of Service and whether you would like to be contacted by the Electronic Frontier Foundation in the future. Next, you will provide the website for which you are activating HTTPS. Finally, you can choose to set up a facility that automatically redirects HTTP connections to HTTPS connections.

        sudo certbot --nginx
        
          
        Saving debug log to /var/log/letsencrypt/letsencrypt.log
        Plugins selected: Authenticator nginx, Installer nginx
        Enter email address (used for urgent renewal and security notices) (Enter 'c' to
        cancel): myemail@example.com
        
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Please read the Terms of Service at
        https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
        agree in order to register with the ACME server at
        https://acme-v02.api.letsencrypt.org/directory
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        (A)gree/(C)ancel: A
        
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Would you be willing to share your email address with the Electronic Frontier
        Foundation, a founding partner of the Let's Encrypt project and the non-profit
        organization that develops Certbot? We'd like to send you email about our work
        encrypting the web, EFF news, campaigns, and ways to support digital freedom.
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        (Y)es/(N)o: N
        
        Which names would you like to activate HTTPS for?
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        1: apache1.example.com
        2: nginx1.example.com
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Select the appropriate numbers separated by commas and/or spaces, or leave input
        blank to select all options shown (Enter 'c' to cancel): 1
        Obtaining a new certificate
        Performing the following challenges:
        http-01 challenge for apache1.example.com
        Waiting for verification...
        Cleaning up challenges
        Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/apache1.example.com
        
        Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        1: No redirect - Make no further changes to the webserver configuration.
        2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
        new sites, or if you're confident your site works on HTTPS. You can undo this
        change by editing your web server's configuration.
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
        Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/apache1.example.com
        
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Congratulations! You have successfully enabled https://apache1.example.com
        
        You should test your configuration at:
        https://www.ssllabs.com/ssltest/analyze.html?d=apache1.example.com
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        
        IMPORTANT NOTES:
         - Congratulations! Your certificate and chain have been saved at:
           /etc/letsencrypt/live/apache1.example.com/fullchain.pem
           Your key file has been saved at:
           /etc/letsencrypt/live/apache1.example.com/privkey.pem
           Your cert will expire on 2019-10-07. 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
        
        
      6. Run certbot as root with the --nginx parameter in order to perform the auto-configuration of Let’s Encrypt for the second website. This is the second time we run certbot, therefore we are asked directly to select the website to configure.

        sudo certbot --nginx
        
          
        Saving debug log to /var/log/letsencrypt/letsencrypt.log
        Plugins selected: Authenticator nginx, Installer nginx
        
        Which names would you like to activate HTTPS for?
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        1: apache1.example.com
        2: nginx1.example.com
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Select the appropriate numbers separated by commas and/or spaces, or leave input
        blank to select all options shown (Enter 'c' to cancel): 2
        Obtaining a new certificate
        Performing the following challenges:
        http-01 challenge for nginx1.example.com
        Waiting for verification...
        Cleaning up challenges
        Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/nginx1.example.com
        
        Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        1: No redirect - Make no further changes to the webserver configuration.
        2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
        new sites, or if you're confident your site works on HTTPS. You can undo this
        change by editing your web server's configuration.
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
        Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/nginx1.example.com
        
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Congratulations! You have successfully enabled https://nginx1.example.com
        
        You should test your configuration at:
        https://www.ssllabs.com/ssltest/analyze.html?d=nginx1.example.com
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        
        IMPORTANT NOTES:
         - Congratulations! Your certificate and chain have been saved at:
           /etc/letsencrypt/live/nginx1.example.com/fullchain.pem
           Your key file has been saved at:
           /etc/letsencrypt/live/nginx1.example.com/privkey.pem
           Your cert will expire on 2019-10-07. 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"
         - 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
        
        
      7. After adding all websites, perform a dry run in order to test the renewal of the certificates. Check that all websites are updating successfully to ensure the automated facility will update the certificates without further effort.

        sudo certbot renew --dry-run
        
          
        Saving debug log to /var/log/letsencrypt/letsencrypt.log
        
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Processing /etc/letsencrypt/renewal/apache1.example.com.conf
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Cert not due for renewal, but simulating renewal for dry run
        Plugins selected: Authenticator nginx, Installer nginx
        Renewing an existing certificate
        Performing the following challenges:
        http-01 challenge for apache1.example.com
        Waiting for verification...
        Cleaning up challenges
        
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        new certificate deployed with reload of nginx server; fullchain is
        /etc/letsencrypt/live/apache1.example.com/fullchain.pem
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Processing /etc/letsencrypt/renewal/nginx1.example.com.conf
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Cert not due for renewal, but simulating renewal for dry run
        Plugins selected: Authenticator nginx, Installer nginx
        Renewing an existing certificate
        Performing the following challenges:
        http-01 challenge for nginx1.example.com
        Waiting for verification...
        Cleaning up challenges
        
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        new certificate deployed with reload of nginx server; fullchain is
        /etc/letsencrypt/live/nginx1.example.com/fullchain.pem
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        ** DRY RUN: simulating 'certbot renew' close to cert expiry
        **          (The test certificates below have not been saved.)
        
        Congratulations, all renewals succeeded. The following certs have been renewed:
          /etc/letsencrypt/live/apache1.example.com/fullchain.pem (success)
          /etc/letsencrypt/live/nginx1.example.com/fullchain.pem (success)
        ** DRY RUN: simulating 'certbot renew' close to cert expiry
        **          (The test certificates above have not been saved.)
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        
        IMPORTANT NOTES:
         - 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.
        
        

        Note

        The certbot package adds a systemd timer in order to activate the automated renewal of Let’s Encrypt certificates. You can view the details of this timer by running systemctl list-timers.

      8. The certbot tool edits and changes the NGINX configuration files of your websites. In doing so, certbot does not obey initial listen directive (listen 80 proxy_protocol;) and does not add the proxy_protocol parameter to the newly added listen 443 ssl; lines. You must edit the configuration files for each website and append “proxy_protocol” to each “listen 443 ssl;” line.

        sudo nano /etc/nginx/sites-enabled/apache1.example.com
        sudo nano /etc/nginx/sites-enabled/nginx1.example.com
        
          
        listen 443 ssl proxy_protocol; # managed by Certbot
        listen [::]:443 ssl proxy_protocol; # managed by Certbot
        
        

        Note

        Each website configuration file has two pairs of listen directives: HTTP and HTTPS, respectively. The first is the original pair for HTTP that was added in a previous section. The second pair was added by certbot for HTTPS. These are pairs because they they cover both IPv4 and IPv6. The notation [::] refers to IPv6. When adding the parameter proxy_protocol, add it before the ; on each line as shown above.

      9. Restart NGINX.

        sudo systemctl restart nginx
        

      Troubleshooting

      Browser Error “SSL_ERROR_RX_RECORD_TOO_LONG”

      You have configured Certbot and created the appropriate Let’s Encrypt configuration for each website. But when you access the website from your browser, you get the following error.

        
      Secure Connection Failed
      
      An error occurred during a connection to apache1.example.com. SSL received a record that exceeded the maximum permissible length. Error code: SSL_ERROR_RX_RECORD_TOO_LONG
      
          The page you are trying to view cannot be shown because the authenticity of the received data could not be verified.
          Please contact the website owners to inform them of this problem.
      
      

      This error is caused when the NGINX reverse proxy in the proxy container does not have the proxy_protocol parameter in the listen 443 directives. Without the parameter, the reverse proxy does not consume the PROXY protocol information before it performs the HTTPS work. It mistakenly passes the PROXY protocol information to the HTTPS module, hence the record too long error.

      Follow the instructions in the previous section and add proxy_protocol to all listen 443 directives. Finally, restart NGINX.

      Error “Unable to connect” or “This site can’t be reached”

      When you attempt to connect to the website from your local computer and receive Unable to connect or This site can’t be reached errors, it is likely the proxy devices have not been configured.

      Run the following command on the host to verify whether LXD is listening and is able to accept connections to ports 80 (HTTP) and 443 (HTTPS).

      sudo ss -ltp '( sport = :http || sport = :https )'
      

      Note

      The ss command is similar to netstat and lsof. It shows information about network connections. In this case, we use it to verify whether there is a service on ports 80 and 443, and which service it is. * -l, to display the listening sockets, * -t, to display only TCP sockets, * -p, to show which processes use those sockets, * ( sport = :http || sport = :https ), to show only ports 80 and 443 (HTTP and HTTPS, respectively).

      In the following output we can verify that both ports 80 and 443 (HTTP and HTTPS, respectively) are in the LISTEN state. In the last column we verify that the process listening is lxd itself.

        
      State     Recv-Q  Send-Q   Local Address:Port   Peer Address:Port
      LISTEN    0       128                  *:http              *:*       users:(("lxd",pid=1301,fd=7),("lxd",pid=1301,fd=5))
      LISTEN    0       128                  *:https             *:*       users:(("lxd",pid=1349,fd=7),("lxd",pid=1349,fd=5))
      
      

      If you see a process listed other than lxd, stop that service and restart the proxy container. By restarting the proxy container, LXD will apply the proxy devices again.

      The Apache access.log Shows the IP Address of the Proxy Container

      You have set up the apache1 container and verified that it is accessible from the internet. But the logs at /var/log/apache2/access.log still show the private IP address of the proxy container, either the private IPv4 (10.x.x.x) or the private IPv6 addresses. What went wrong?

      The default log formats for printing access logs in Apache only print the IP address of the host of the last hop (i.e. the proxy server). This is the %h format specifier as shown below.

        
      LogFormat "%v:%p %h %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"" vhost_combined
      LogFormat "%h %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"" combined
      LogFormat "%h %l %u %t "%r" %>s %O" common
      
      

      The %h must be manually replaced with the %a format specifier, which prints the value as returned by the real RemoteIP Apache module.

        
      LogFormat "%v:%p %a %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"" vhost_combined
      LogFormat "%a %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"" combined
      LogFormat "%a %l %u %t "%r" %>s %O" common
      
      
      1. Run the following command in the apache1 container to edit the configuration file httpd.conf and perform the change from %h to %a.

        sudo nano /etc/apache2/apache2.conf
        
      2. Reload the Apache web server service.

        sudo systemctl reload apache2
        

      Next Steps

      You have set up a reverse proxy to host many websites on the same server and installed each website in a separate container. You can install static or dynamic websites in the containers. For dynamic websites, you may need additional configuration; check the respective documentation for setup using a reverse proxy. In addition, you may also use NGINX as a reverse proxy for non-HTTP(S) services.

      More Information

      You may wish to consult the following resources for additional information on this topic. While these are provided in the hope that they will be useful, please note that we cannot vouch for the accuracy or timeliness of externally hosted materials.

      Find answers, ask questions, and help others.

      This guide is published under a CC BY-ND 4.0 license.



      Source link

      How To Set Up a Jupyter Notebook with Python 3 on Debian 10


      Introduction

      Jupyter Notebook offers a command shell for interactive computing as a web application so that you can share and communicate with code. The tool can be used with several languages, including Python, Julia, R, Haskell, and Ruby. It is often used for working with data, statistical modeling, and machine learning.

      This tutorial will walk you through setting up Jupyter Notebook to run from a Debian 10 server, as well as teach you how to connect to and use the Notebook. Jupyter Notebooks (or just “Notebooks”) are documents produced by the Jupyter Notebook app which contain both computer code and rich text elements (paragraph, equations, figures, links, etc.) which aid in presenting and sharing reproducible research.

      By the end of this guide, you will be able to run Python 3 code using Jupyter Notebook running on a remote Debian 10 server.

      Prerequisites

      In order to complete this guide, you should have a fresh Debian 10 server instance with a basic firewall and a non-root user with sudo privileges configured. You can learn how to set this up by running through our Initial Server Setup with Debian 10 guide.

      Step 1 — Install Pip and Python Headers

      To begin the process, we’ll download and install all of the items we need from the Debian repositories. We will use the Python package manager pip to install additional components a bit later.

      We first need to update the local apt package index and then download and install the packages:

      Next, install pip and the Python header files, which are used by some of Jupyter’s dependencies:

      • sudo apt install python3-pip python3-dev

      Debian 10 (“Buster”) comes preinstalled with Python 3.7.

      We can now move on to setting up a Python virtual environment into which we’ll install Jupyter.

      Step 2 — Create a Python Virtual Environment for Jupyter

      Now that we have Python 3, its header files, and pip ready to go, we can create a Python virtual environment for easier management. We will install Jupyter into this virtual environment.

      To do this, we first need access to the virtualenv command. We can install this with pip.

      Upgrade pip and install the package by typing:

      • sudo -H pip3 install --upgrade pip
      • sudo -H pip3 install virtualenv

      With virtualenv installed, we can start forming our environment. Create and move into a directory where we can keep our project files:

      • mkdir ~/myprojectdir
      • cd ~/myprojectdir

      Within the project directory, create a Python virtual environment by typing:

      This will create a directory called myprojectenv within your myprojectdir directory. Inside, it will install a local version of Python and a local version of pip. We can use this to install and configure an isolated Python environment for Jupyter.

      Before we install Jupyter, we need to activate the virtual environment. You can do that by typing:

      • source myprojectenv/bin/activate

      Your prompt should change to indicate that you are now operating within a Python virtual environment. It will look something like this: (myprojectenv)user@host:~/myprojectdir$.

      You’re now ready to install Jupyter into this virtual environment.

      Step 3 — Install Jupyter

      With your virtual environment active, install Jupyter with the local instance of pip:

      Note: When the virtual environment is activated (when your prompt has (myprojectenv) preceding it), use pip instead of pip3, even if you are using Python 3. The virtual environment’s copy of the tool is always named pip, regardless of the Python version.

      At this point, you’ve successfully installed all the software needed to run Jupyter. We can now start the Notebook server.

      Step 4 — Run Jupyter Notebook

      You now have everything you need to run Jupyter Notebook! To run it, execute the following command:

      A log of the activities of the Jupyter Notebook will be printed to the terminal. When you run Jupyter Notebook, it runs on a specific port number. The first Notebook you run will usually use port 8888. To check the specific port number Jupyter Notebook is running on, refer to the output of the command used to start it:

      Output

      [I 21:23:21.198 NotebookApp] Writing notebook server cookie secret to /run/user/1001/jupyter/notebook_cookie_secret [I 21:23:21.361 NotebookApp] Serving notebooks from local directory: /home/sammy/myprojectdir [I 21:23:21.361 NotebookApp] The Jupyter Notebook is running at: [I 21:23:21.361 NotebookApp] http://localhost:8888/?token=1fefa6ab49a498a3f37c959404f7baf16b9a2eda3eaa6d72 [I 21:23:21.361 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation). [W 21:23:21.361 NotebookApp] No web browser found: could not locate runnable browser. [C 21:23:21.361 NotebookApp] Copy/paste this URL into your browser when you connect for the first time, to login with a token: http://localhost:8888/?token=1fefa6ab49a498a3f37c959404f7baf16b9a2eda3eaa6d72

      If you are running Jupyter Notebook on a local Debian computer (not on a Droplet), you can simply navigate to the displayed URL to connect to Jupyter Notebook. If you are running Jupyter Notebook on a Droplet, you will need to connect to the server using SSH tunneling as outlined in the next section.

      At this point, you can keep the SSH connection open and keep Jupyter Notebook running or can exit the app and re-run it once you set up SSH tunneling. Let’s keep it simple and stop the Jupyter Notebook process. We will run it again once we have SSH tunneling working. To stop the Jupyter Notebook process, press CTRL+C, type Y, and hit ENTER to confirm. The following will be displayed:

      Output

      [C 21:28:28.512 NotebookApp] Shutdown confirmed [I 21:28:28.512 NotebookApp] Shutting down 0 kernels

      We’ll now set up an SSH tunnel so that we can access the Notebook.

      Step 5 — Connect to the Server Using SSH Tunneling

      In this section we will learn how to connect to the Jupyter Notebook web interface using SSH tunneling. Since Jupyter Notebook will run on a specific port on the server (such as :8888, :8889 etc.), SSH tunneling enables you to connect to the server’s port securely.

      The next two subsections describe how to create an SSH tunnel from 1) a Mac or Linux and 2) Windows. Please refer to the subsection for your local computer.

      SSH Tunneling with a Mac or Linux

      If you are using a Mac or Linux, the steps for creating an SSH tunnel are similar to using SSH to log in to your remote server, except that there are additional parameters in the ssh command. This subsection will outline the additional parameters needed in the ssh command to tunnel successfully.

      SSH tunneling can be done by running the following SSH command in a new local terminal window:

      • ssh -L 8888:localhost:8888 your_server_username@your_server_ip

      The ssh command opens an SSH connection, but -L specifies that the given port on the local (client) host is to be forwarded to the given host and port on the remote side (server). This means that whatever is running on the second port number (e.g. 8888) on the server will appear on the first port number (e.g. 8888) on your local computer.

      Optionally change port 8888 to one of your choosing to avoid using a port already in use by another process.

      server_username is your username (e.g. sammy) on the server which you created and your_server_ip is the IP address of your server.

      For example, for the username sammy and the server address 203.0.113.0, the command would be:

      • ssh -L 8888:localhost:8888 sammy@203.0.113.0

      If no error shows up after running the ssh -L command, you can move into your programming environment and run Jupyter Notebook:

      You’ll receive output with a URL. From a web browser on your local machine, open the Jupyter Notebook web interface with the URL that starts with http://localhost:8888. Ensure that the token number is included, or enter the token number string when prompted at http://localhost:8888.

      SSH Tunneling with Windows and Putty

      If you are using Windows, you can create an SSH tunnel using Putty.

      First, enter the server URL or IP address as the hostname as shown:

      Set Hostname for SSH Tunnel

      Next, click SSH on the bottom of the left pane to expand the menu, and then click Tunnels. Enter the local port number to use to access Jupyter on your local machine. Choose 8000 or greater to avoid ports used by other services, and set the destination as localhost:8888 where :8888 is the number of the port that Jupyter Notebook is running on.

      Now click the Add button, and the ports should appear in the Forwarded ports list:

      Forwarded ports list

      Finally, click the Open button to connect to the server via SSH and tunnel the desired ports. Navigate to http://localhost:8000 (or whatever port you chose) in a web browser to connect to Jupyter Notebook running on the server. Ensure that the token number is included, or enter the token number string when prompted at http://localhost:8000.

      Step 6 — Using Jupyter Notebook

      This section goes over the basics of using Jupyter Notebook. If you don’t currently have Jupyter Notebook running, start it with the jupyter notebook command.

      You should now be connected to it using a web browser. Jupyter Notebook is a very powerful tool with many features. This section will outline a few of the basic features to get you started using the Notebook. Jupyter Notebook will show all of the files and folders in the directory it is run from, so when you’re working on a project make sure to start it from the project directory.

      To create a new Notebook file, select New > Python 3 from the top right pull-down menu:

      Create a new Python 3 notebook

      This will open a Notebook. We can now run Python code in the cell or change the cell to markdown. For example, change the first cell to accept Markdown by clicking Cell > Cell Type > Markdown from the top navigation bar. We can now write notes using Markdown and even include equations written in LaTeX by putting them between the $$ symbols. For example, type the following into the cell after changing it to markdown:

      # First Equation
      
      Let us now implement the following equation:
      $$ y = x^2$$
      
      where $x = 2$
      

      To turn the markdown into rich text, press CTRL+ENTER, and the following should be the results:

      results of markdown

      You can use the markdown cells to make notes and document your code. Let’s implement that equation and print the result. Click on the top cell, then press ALT+ENTER to add a cell below it. Enter the following code in the new cell.

      x = 2
      y = x**2
      print(y)
      

      To run the code, press CTRL+ENTER. You’ll receive the following results:

      first equation results

      You now have the ability to import modules and use the Notebook as you would with any other Python development environment!

      Conclusion

      At this point, you should be able to write reproducible Python code and notes in Markdown using Jupyter Notebook. To get a quick tour of Jupyter Notebook from within the interface, select Help > User Interface Tour from the top navigation menu to learn more.

      From here, you can begin a data analysis and visualization project by reading Data Analysis and Visualization with pandas and Jupyter Notebook in Python 3.



      Source link

      How To Set Filesystem Quotas on Debian 10


      Introduction

      Quotas are used to limit the amount of disk space a user or group can use on a filesystem. Without such limits, a user could fill up the machine’s disk and cause problems for other users and services.

      In this tutorial we will install command line tools to create and inspect disk quotas, then set a quota for an example user.

      Prerequisites

      This tutorial assumes you are logged into a Debian 10 server, with a non-root, sudo-enabled user, as described in Initial Server Setup with Debian 10.

      The techniques in this tutorial should generally work on Linux distributions other than Debian, but may require some adaptation.

      To set and check quotas, we first need to install the quota command line tools using apt. Let’s update our package list, then install the package:

      sudo apt update
      sudo apt install quota
      

      You can verify that the tools are installed by running the quota command and asking for its version information:

      quota --version
      

      Output

      Quota utilities version 4.04. . . .

      It’s fine if your output shows a slightly different version number.

      Next we will update our filesystem’s mount options to enable quotas on our root filesystem.

      Step 2 – Updating Filesystem Mount Options

      To activate quotas on a particular filesystem, we need to mount it with a few quota-related options specified. We do this by updating the filesystem’s entry in the /etc/fstab configuration file. Open that file in your favorite text editor now:

      sudo nano /etc/fstab
      

      The file’s contents will be similar to the following:

      /etc/fstab

      # /etc/fstab: static file system information.
      UUID=06b2aae3-b525-4a4c-9549-0fc6045bd08e   / ext4    errors=remount-ro   0   1
      

      This fstab file is from a virtual server. A desktop or laptop computer will probably have a slightly different looking fstab, but in most cases you’ll have a / or root filesystem that represents all of your disk space.

      Update the line pointing to the root filesystem by adding options as follows:

      /etc/fstab

      # /etc/fstab: static file system information.
      UUID=06b2aae3-b525-4a4c-9549-0fc6045bd08e   /   ext4    errors=remount-ro,usrquota,grpquota   0   1
      

      You will add the new options to the end of any existing options, being sure to separate them all with a comma and no spaces. The above change will allow us to enable both user- (usrquota) and group-based (grpquota) quotas on the filesystem. If you only need one or the other, you may leave out the unused option.

      Remount the filesystem to make the new options take effect:

      sudo mount -o remount /
      

      Note: Be certain there are no spaces between the options listed in your /etc/fstab file. If you put a space after the , comma, you will see an error like the following:

      Output

      mount: /etc/fstab: parse error at line 2 -- ignored

      If you see this message after running the previous mount command, reopen the fstab file, correct any errors, and repeat the mount command before continuing.

      We can verify that the new options were used to mount the filesystem by looking at the /proc/mounts file. Here, we use grep to show only the root filesystem entry in that file:

      cat /proc/mounts | grep ' / '
      

      Output

      /dev/vda1 / ext4 rw,relatime,quota,usrquota,grpquota,errors=remount-ro,data=ordered 0 0

      Note the two options that we specified. Now that we’ve installed our tools and updated our filesystem options, we can turn on the quota system.

      Step 3 – Enabling Quotas

      Before finally turning on the quota system, we need to manually run the quotacheck command once:

      sudo quotacheck -ugm /
      

      This command creates the files /aquota.user and /aquota.group. These files contain information about the limits and usage of the filesystem, and they need to exist before we turn on quota monitoring. The quotacheck parameters we’ve used are:

      • u: specifies that a user-based quota file should be created
      • g: indicates that a group-based quota file should be created
      • m: disables remounting the filesystem as read-only while performing the initial tallying of quotas. Remounting the filesystem as read-only will give more accurate results in case a user is actively saving files during the process, but is not necessary during this initial setup.

      If you don’t need to enable user- or group-based quotas, you can leave off the corresponding quotacheck option.

      We can verify that the appropriate files were created by listing the root directory:

      ls /
      

      Output

      aquota.group bin dev home initrd.img.old lib64 media opt root sbin sys usr vmlinuz aquota.user boot etc initrd.img lib lost+found mnt proc run srv tmp var vmlinuz.old

      If you didn’t include the u or g options in the quotacheck command, the corresponding file will be missing. Now we’re ready to turn on the quota system:

      sudo quotaon -v /
      

      Output

      /dev/vda1 [/]: group quotas turned on /dev/vda1 [/]: user quotas turned on

      Our server is now monitoring and enforcing quotas, but we’ve not set any yet! Next we’ll set a disk quota for a single user.

      Step 4 – Configuring Quotas for a User

      There are a few ways we can set quotas for users or groups. Here, we’ll go over how to set quotas with both the edquota and setquota commands.

      Using edquota to Set a User Quota

      We use the edquota command to edit quotas. Let’s edit our example sammy user’s quota:

      sudo edquota -u sammy
      

      The -u option specifies that this is a user quota we’ll be editing. If you’d like to edit a group’s quota instead, use the -g option in its place.

      This will open up a file in the default text editor, similar to how crontab -e opens a temporary file for you to edit. The file will look similar to this:

      Disk quotas for user sammy (uid 1001):
        Filesystem                   blocks       soft       hard     inodes     soft     hard
        /dev/vda1                        24          0          0          7        0        0
      

      This lists the username and uid, the filesystems that have quotas enabled on them, and the block– and inode-based usage and limits. Setting an inode-based quota would limit how many files and directories a user can create, regardless of the amount of disk space they use. Most people will want block-based quotas, which specifically limit disk space usage. This is what we will configure.

      Note: The concept of a block is poorly specified and can change depending on many factors, including which command line tool is reporting them. In the context of setting quotas on Debian, it’s fairly safe to assume that 1 block equals 1 kilobyte of disk space.

      In the above listing, our user sammy is using 24 blocks, or 24KB of space on the /dev/vda1 drive. The soft and hard limits are both disabled with a 0 value.

      Each type of quota allows you to set both a soft limit and a hard limit. When a user exceeds the soft limit, they are over quota, but they are not immediately prevented from consuming more space or inodes. Instead, some leeway is given: the user has – by default – seven days to get their disk use back under the soft limit. At the end of the seven day grace period, if the user is still over the soft limit it will be treated as a hard limit. A hard limit is less forgiving: all creation of new blocks or inodes is immediately halted when you hit the specified hard limit. This behaves as if the disk is completely out of space: writes will fail, temporary files will fail to be created, and the user will start to see warnings and errors while performing common tasks.

      Let’s update our sammy user to have a block quota with a 100MB soft limit, and a 110MB hard limit:

      Disk quotas for user sammy (uid 1001):
        Filesystem                   blocks       soft       hard     inodes     soft     hard
        /dev/vda1                        24       100M       110M          7        0        0
      

      Save and close the file. To check the new quota we can use the quota command:

      sudo quota -vs sammy
      

      Output

      Disk quotas for user sammy (uid 1001): Filesystem space quota limit grace files quota limit grace /dev/vda1 24K 100M 110M 7 0 0

      The command outputs our current quota status, and shows that our quota is 100M while our limit is 110M. This corresponds to the soft and hard limits respectively.

      Note: If you want your users to be able to check their own quotas without having sudo access, you’ll need to give them permission to read the quota files we created in Step 4. One way to do this would be to make a users group, make those files readable by the users group, and then make sure all your users are also placed in the group.

      To learn more about Linux permissions, including user and group ownership, please read An Introduction to Linux Permissions

      Using setquota to Set a User Quota

      Unlike edquota, setquota will update our user’s quota information in a single command, without an interactive editing step. We will specify the username and the soft and hard limits for both block- and inode-based quotas, and finally the filesystem to apply the quota to:

      sudo setquota -u sammy 200M 220M 0 0 /
      

      The above command will double sammy‘s block-based quota limits to 200 megabytes and 220 megabytes. The 0 0 for inode-based soft and hard limits indicates that they remain unset. This is required even if we’re not setting any inode-based quotas.

      Once again, use the quota command to check our work:

      sudo quota -vs sammy
      

      Output

      Disk quotas for user sammy (uid 1001): Filesystem space quota limit grace files quota limit grace /dev/vda1 24K 200M 220M 7 0 0

      Now that we have set some quotas, let’s find out how to generate a quota report.

      Step 5 – Generating Quota Reports

      To generate a report on current quota usage for all users on a particular filesystem, use the repquota command:

      sudo repquota -s /
      

      Output

      *** Report for user quotas on device /dev/vda1 Block grace time: 7days; Inode grace time: 7days Space limits File limits User used soft hard grace used soft hard grace ---------------------------------------------------------------------- root -- 981M 0K 0K 35234 0 0 nobody -- 7664K 0K 0K 3 0 0 ntp -- 12K 0K 0K 3 0 0 _apt -- 8K 0K 0K 2 0 0 debian -- 16K 0K 0K 4 0 0 sammy -- 24K 200M 220M 7 0 0

      In this instance we’re generating a report for the / root filesystem. The -s command tells repquota to use human-readable numbers when possible. There are a few system users listed, which probably have no quotas set by default. Our user sammy is listed at the bottom, with the amounts used and soft and hard limits.

      Also note the Block grace time: 7days callout, and the grace column. If our user was over the soft limit, the grace column would show how much time they had left to get back under the limit.

      In the next step we’ll update the grace periods for our quota system.

      Step 6 – Configuring a Grace Period for Overages

      We can configure the period of time where a user is allowed to float above the soft limit. We use the setquota command to do so:

      sudo setquota -t 864000 864000 /
      

      The above command sets both the block and inode grace times to 864000 seconds, or 10 days. This setting applies to all users, and both values must be provided even if you don’t use both types of quota (block vs. inode).

      Note that the values must be specified in seconds.

      Run repquota again to check that the changes took effect:

      sudo repquota -s /
      

      Output

      Block grace time: 10days; Inode grace time: 10days . . .

      The changes should be reflected immediately in the repquota output.

      Conclusion

      In this tutorial we installed the quota command line tools, set up a block-based quota for one user, and generated a report on our filesystem’s quota usage.

      The following are some common errors you may see when setting up and manipulating filesystem quotas.

      quotaon Output

      quotaon: cannot find //aquota.group on /dev/vda1 [/] quotaon: cannot find //aquota.user on /dev/vda1 [/]

      This is an error you might see if you tried to turn on quotas (using quotaon) before running the initial quotacheck command. The quotacheck command creates the aquota or quota files needed to turn on the quota system. See Step 3 for more information.

      quota Output

      quota: Cannot open quotafile //aquota.user: Permission denied quota: Cannot open quotafile //aquota.user: Permission denied quota: Cannot open quotafile //quota.user: No such file or directory

      This is the error you’ll see if you run quota and your current user does not have permission to read the quota files for your filesystem. You (or your system administrator) will need to adjust the file permissions appropriately, or use sudo when running commands that require access to the quota file.

      To learn more about Linux permissions, including user and group ownership, please read An Introduction to Linux Permissions



      Source link