Install Polr with a LEMP Stack on Ubuntu 20.04
Introduction
Polr is a free and open-source link shortener written in PHP and powered by the Lumen framework. It allows you to host your URL shortener and to gain control over your data. Its significant features include a management dashboard, detailed link analytics, and a robust API.
This tutorial guides you through the process of installing Polr on Ubuntu 20.04 LTS and setting up HTTPS with a free Let's Encrypt TLS certificate.
Prerequisites
- A Ubuntu 20.04 server.
- Follow Vultr's best practices guides to create a sudo user and update the Ubuntu server.
- (Optional) Configure the Ubuntu firewall with ports 80, 443, and 22 open.
This tutorial assumes you own a domain name such as example.com, and you have pointed it to the server IP address. If not, replace example.com with the server IP address.
Make sure to replace example.com in the code examples with your domain name or IP address.
1. Install PHP
At the time of this writing, the latest version of PHP compatible with Polr is 7.3. But, the official Ubuntu 20.04 repositories only offer PHP 7.4. Therefore, you must use another 3rd-party repository to install PHP 7.3.
ppa:ondrej/php is an Ubuntu repository developed by Ondřej Surý, a long-time Debian developer. It offers both PHP 5.6, PHP 7.0 – 7.4, and PHP 8.0. You can install PHP 7.3 now, and when Polr supports newer PHP versions, you can also install them.
Log in to the server as a non-root sudo user via SSH.
Add the
ppa:ondrej/php
repository.$ sudo LC_ALL=C.UTF-8 add-apt-repository -y ppa:ondrej/php
Install PHP-FPM and other necessary PHP extensions.
$ sudo apt -y install php7.3-cli php7.3-common php7.3-curl php7.3-fpm php7.3-json php7.3-mbstring php7.3-mysql php7.3-xml
List all the time zones that your Ubuntu system supports. Use the Up / Down keys to move through the list, and press Q to exit.
$ timedatectl list-timezones
Copy an appropriate time zone from the list, for example, America/New_York. Then update your Ubuntu system with that time zone.
$ sudo timedatectl set-timezone America/New_York
Edit the main PHP configuration file to tell PHP to use the new time zone. This tutorial uses
nano
as the editor, but you can use another editor such asvim
.$ sudo nano /etc/php/7.3/fpm/php.ini
Find the line
;date.timezone =
. Remove the semicolon and add your time zone. For example:date.timezone = America/New_York
Save the configuration file and exit.
To enhance the security of your server, create a dedicated user named
polr
as the user/group of PHP-FPM processes for Polr. This user also owns the Polr source code files/folders.$ sudo adduser polr
Every time you want to add, delete, or update the source code files/folders, you need to switch to this user.
Create the PHP-FPM configuration file from the default one.
$ sudo cp /etc/php/7.3/fpm/pool.d/www.conf /etc/php/7.3/fpm/pool.d/polr.conf
Rename the default file to disable it and keep it as a backup.
$ sudo mv /etc/php/7.3/fpm/pool.d/www.conf /etc/php/7.3/fpm/pool.d/www.conf.default
Edit the PHP-FPM configuration file.
$ sudo nano /etc/php/7.3/fpm/pool.d/polr.conf
In the configuration file, any line starting with
;
is a comment.Search for the following settings, then:
- Replace
[www]
with[polr]
- Replace
user = www-data
withuser = polr
- Replace
group = www-data
withgroup = polr
(do not touch thelisten.group = www-data
setting)
Make sure the
listen = /run/php/php7.3-fpm.sock
setting does not start with;
. This setting makes PHP-FPM listen on a Unix socket specified by the/run/php/php7.3-fpm.sock
file.- Replace
Copy and paste the following settings to the end of the file.
catch_workers_output = yes php_flag[display_errors] = off php_admin_value[error_log] = /var/log/fpm-php/polr/error.log php_admin_flag[log_errors] = on php_admin_value[session.save_path] = /var/lib/php/session.polr
Those settings make PHP-FPM log error messages to the
/var/log/fpm-php/polr/error.log
file instead of displaying them to website users and store session data in the/var/lib/php/session.polr
folder.Save the configuration file and exit.
Create two folders to store PHP logs and session data.
$ sudo mkdir -p /var/log/fpm-php/polr $ sudo mkdir -p /var/lib/php/session.polr
Update the ownership and permissions of the two folders so that only the PHP-FPM processes of Polr can write to them.
$ sudo chown polr:polr /var/log/fpm-php/polr $ sudo chmod 700 /var/log/fpm-php/polr $ sudo chown polr:polr /var/lib/php/session.polr $ sudo chmod 700 /var/lib/php/session.polr
Check the new configuration.
$ sudo php-fpm7.3 -t
Restart the PHP-FPM service for the changes to take effect.
$ sudo systemctl restart php7.3-fpm.service
2. Install MySQL
The minimum version of MySQL supported by Polr is 5.5. However, you can install MySQL version 8.0 from the official Ubuntu 20.04 repositories to fulfill this requirement.
$ sudo apt -y install mysql-server
Run the
mysql_secure_installation
script to enhance the security of MySQL.$ sudo mysql_secure_installation
To enforce strong passwords, press Y and Enter to set up the
VALIDATE PASSWORD
component. Then press 1 and Enter to select theMEDIUM
password policy, which is secure enough.With this policy, MySQL requires all passwords to includes numeric, mixed case, and special characters. You can use a free password manager like KeePassXC or an online tool such as Random Password Generator to generate strong passwords.
Enter a strong password twice for the MySQL
root
user. Then press Y and Enter to confirm.Press Y and Enter for any remaining questions to accept the recommended options.
Connect to the MySQL command line as the MySQL
root
user.$ sudo mysql -u root
Create a MySQL database named polr for Polr.
mysql> CREATE DATABASE polr CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Create a MySQL user named polr for Polr. Replace password with a strong password.
mysql> CREATE USER 'polr'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password'; mysql> GRANT ALL PRIVILEGES ON polr.* TO 'polr'@'localhost'; mysql> FLUSH PRIVILEGES;
Exit the MySQL command line.
mysql> exit
3. Install Polr
Download the Source Code
Retrieve the code from GitHub.
$ cd ~ && wget https://github.com/cydrobolt/polr/archive/refs/tags/2.3.0b.zip
At the time of this writing, the latest stable version of Polr is 2.3.0b. Of course, you can always visit the Polr releases page on GitHub to get the latest version.
Install the
unzip
package to extract the archive.$ sudo apt -y install unzip
Extract the archive.
$ unzip 2.3.0b.zip
Set
polr
as the owner of the source code folder.$ sudo chown -R polr:polr polr*
Move the source code folder to
/var/www/polr
because, traditionally, the source code folders of websites are in the/var/www
folder.$ sudo mkdir /var/www $ sudo mv polr* /var/www/polr
Install Dependencies
Polr requires Composer, the PHP dependency manager, to manage its dependencies.
Install Composer with the following command.
$ cd ~ && curl -sS https://getcomposer.org/installer | php
Make the
composer
command globally available.$ sudo mv composer.phar /usr/local/bin/composer
Switch to the
polr
user before installing Polr dependencies.$ sudo su polr
Install dependencies with Composer.
$ cd /var/www/polr && composer install --no-dev -o
Copy the provided configuration file to enable the web-based installer.
$ cp .env.setup .env
Switch back to the sudo user to continue with the setup.
$ exit
4. Install Nginx
Install Nginx with the following command.
$ sudo apt -y install nginx
Disable the default configuration.
$ sudo rm /etc/nginx/sites-enabled/default
Create a new configuration file for Polr.
$ sudo nano /etc/nginx/sites-available/polr-http.conf
Paste the following contents:
server { listen 80; listen [::]:80; server_name example.com; root /var/www/polr/public; index index.html index.php; # All URLs are processed by index.php location / { try_files $uri $uri/ /index.php$is_args$args; } # Pass PHP files to PHP-FPM listening on /run/php/php7.3-fpm.sock location ~ \.php$ { try_files $uri =404; # Mitigate https://httpoxy.org/ vulnerabilities fastcgi_param HTTP_PROXY ""; fastcgi_pass unix:/run/php/php7.3-fpm.sock; fastcgi_index index.php; include fastcgi.conf; } # Set expiration of assets to MAX for caching location ~* \.(jpg|jpeg|gif|png|css|js|ico|svg|eot|ttf|woff|woff2|otf)$ { expires max; log_not_found off; } }
Save the configuration file and exit.
Enable the new configuration.
$ sudo ln -s /etc/nginx/sites-available/polr-http.conf /etc/nginx/sites-enabled/polr-http.conf
Add the
www-data
user to thepolr
group so that Nginx processes can access the Polr source code folder.$ sudo usermod -aG polr www-data
Check the new configuration.
$ sudo nginx -t
Reload the Nginx service for the changes to take effect.
$ sudo systemctl reload nginx.service
5. (Optional) Configure HTTPS
If you own a valid domain name, you can set up HTTPS for your Polr at no cost. Using the Certbot program, you can get a free TLS certificate from Let's Encrypt, a certificate authority.
Install Certbot with Snap
Snap Store is an app store for Linux with millions of users. Ubuntu has built-in Snap Store support that makes it easy to get the latest version of Certbot with features like automatic certificate renewal.
$ sudo snap install --classic certbot
Make the certbot
command globally available.
$ sudo ln -s /snap/bin/certbot /usr/bin/certbot
Get a Let's Encrypt Certificate
Rename the HTTP configuration file to make it the template for the HTTPS configuration file.
$ sudo mv /etc/nginx/sites-available/polr-http.conf /etc/nginx/sites-available/polr-https.conf
Create a new configuration file to serve HTTP requests.
$ sudo nano /etc/nginx/sites-available/polr-http.conf
Paste the following contents:
server { listen 80; listen [::]:80; server_name example.com; root /var/www/polr/public; location / { return 301 https://$server_name$request_uri; } location /.well-known/acme-challenge/ {} }
This configuration makes Nginx redirect all HTTP requests, except those from Let's Encrypt, to corresponding HTTPS requests.
Save the configuration file and exit. Then check the Nginx configuration.
$ sudo nginx -t
Apply the new configuration.
$ sudo systemctl reload nginx.service
Run the following command to get the Let's Encrypt certificate.
$ sudo certbot certonly --webroot -w /var/www/polr/public -d example.com -m admin@example.com --agree-tos
You may need to answer a question about sharing your email with the Electronic Frontier Foundation.
When finished, certbot
tells you the path of your certificate file and key file:
/etc/letsencrypt/live/example.com/fullchain.pem
/etc/letsencrypt/live/example.com/privkey.pem
Another critical file, located in the same folder, also needed for the next step, is chain.pem
.
Install the Certificate with Nginx
Generate a file with DH parameters for DHE ciphers.
$ sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048
2048 is the recommended size of DH parameters. This process may take a while, so please be patient.
Update the HTTPS configuration file.
$ sudo nano /etc/nginx/sites-available/polr-https.conf
Find the following lines:
listen 80; listen [::]:80;
Replace them with:
listen 443 ssl http2; listen [::]:443 ssl http2; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; ssl_session_timeout 1d; ssl_session_cache shared:MozSSL:10m; # about 40000 sessions # DH parameters file ssl_dhparam /etc/nginx/dhparam.pem; # intermediate configuration ssl_protocols TLSv1.2; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; # HSTS (ngx_http_headers_module is required) (63072000 seconds) # # Uncomment the following line only if your website fully supports HTTPS # and you have no intention of going back to HTTP, otherwise, it will # break your site. # # add_header Strict-Transport-Security "max-age=63072000" always; # OCSP stapling ssl_stapling on; ssl_stapling_verify on; # verify chain of trust of OCSP response using Root CA and Intermediate certs ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem; # Use Cloudflare DNS resolver resolver 1.1.1.1;
Save the configuration file and exit.
Enable the new configuration.
$ sudo ln -s /etc/nginx/sites-available/polr-https.conf /etc/nginx/sites-enabled/polr-https.conf
Check the Nginx configuration.
$ sudo nginx -t
Apply the new configuration.
$ sudo systemctl reload nginx.service
Automate Renewal
Let's Encrypt certificates are valid for 90 days, so you must renew your TLS certificate at least once every three months. The Certbot installation automatically created a systemd timer unit to automate this task.
Run the following command to verify the timer is active.
$ sudo systemctl list-timers | grep 'certbot\|ACTIVATES'
After renewing the certificate, Certbot will not automatically reload Nginx, so Nginx still uses the old certificate. Instead, you must write a script inside the
/etc/letsencrypt/renewal-hooks/deploy
folder to reload Nginx.Open your text editor.
$ sudo nano /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
Paste the following contents:
#!/bin/bash /usr/bin/systemctl reload nginx.service
Save and exit.
Make the script executable.
$ sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
Test the renewal process with a dry run.
$ sudo certbot renew --dry-run
This Vultr article explains all the above steps in more detail. This kind of TLS setup gives you an A on the SSL Labs test.
6. Complete the Polr Setup
Restart the server.
$ sudo reboot
Wait a moment for the system to boot, then open the http://example.com link in your browser.
The Setup screen appears with many settings. Here are the fundamental ones:
- Database Username: enter polr
- Database Password: enter the database password you created in step 2
- Database Name: enter polr
- Application Protocol: enter https:// if you have set up HTTPS in step 5. If not, enter http://
- Application URL: enter example.com
- Shortening Permissions: choose between Anyone can shorten URLs and Only logged in users may shorten URLs
- Admin Username: enter a desired name
- Admin Password: enter a strong password
For the remaining settings, customize them to fit your specific needs or accept their default values.
Click the Install button at the bottom of the screen to start the installation. When finished, Polr redirects you to the Setup Complete screen.
Behind the scenes, Polr has just saved all the settings in the /var/www/polr/.env
configuration file. If you have programming experience, you can edit this file to change the settings as needed.
Your Polr website is now ready. You may log in or access the home page to create shortened links.