Install and run a production server for a Laravel application
You already own:
- A domain name
- A Github account
- A fresh Ubuntu 16.04 Server with root access
You want to deploy an application for:
- Laravel with a PostgreSQL database
- Running on PHP7 and served by Nginx
- Optionally, Redis for Queue, Session and Cache
If so, let’s go, this is for you.
Recipe
Let's say:
- raphael is your name.
- nantes is your hostname.
- myapp is your app name.
You will have to replace each occurrence of theses values in the code below.
This recipe has 11 steps, it may take half an hour to make it.
- Initialization
- Composer
- PostgreSQL
- Firewall
- Application user
- Facteur
- Laravel
- Nginx
- Your user
- Disable password auth
- Https
Initialization
Connect to your server with root user. Then, name your host
hostname nantes
Install packages (PHP7, Nginx, PostgreSQL, etc.)
apt update
apt upgrade
apt install php php-mbstring php-xml php-zip git nginx redis-server postgresql fail2ban htop php-pgsql
You can go to your server IP with your browser, you should see a welcome message from Nginx
Composer
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php composer-setup.php
php -r "unlink('composer-setup.php');"
mv composer.phar /usr/local/bin/composer
PostgreSQL
Run psql
:
sudo -u postgres psql
Create the user (don't forget to use your own password):
CREATE ROLE myapp LOGIN UNENCRYPTED PASSWORD 'xxxxx' SUPERUSER INHERIT NOCREATEDB NOCREATEROLE NOREPLICATION;
Write down the password, you will need it later. Disconnect from psql
with \q
. Then restart service and create the database:
service postgresql restart
sudo -u postgres /usr/bin/createdb --echo --owner=myapp myapp
Firewall
Configure and enable ufw
ufw allow 443
ufw allow 80
ufw allow 22
ufw enable
Application user
You need to create an application user. It's not your name. Replace myapp everywhere with the name of your application.
useradd myapp
mkdir -p /home/myapp
chsh -s /bin/bash myapp
chown -R myapp:myapp /home/myapp
sudo -u myapp ssh-keygen -t rsa -b 4096 -C "myapp"
Optional step: Copy your public key, located in /home/myapp/.ssh/id_rsa.pub
, to your github account or repository (only if the repository is private). It will be required to make continuous delivery work without connecting to your server.
Facteur
Facteur is a small tool for initializing and deploying Laravel applications. Install it.
cd /home/myapp
wget https://github.com/rap2hpoutre/facteur/releases/download/v0.1.0/facteur
chmod +x facteur
mv facteur /usr/local/bin/facteur
Init your app with deployer (replace https://github.com/your/app.git
with the URL to your git repo).
sudo -u myapp HOME=/home/myapp facteur init www https://github.com/your/app.git
Laravel
Copy .env.example
to .env
sudo -u myapp cp /home/myapp/www/current/.env.example /home/myapp/www/current/.env
Edit /home/myapp/www/current/.env
vi /home/myapp/www/current/.env
Change the values of DB_*
with your credentials.
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=myapp
DB_USERNAME=myapp
DB_PASSWORD=xxxxx
Change APP_ENV=local
to APP_ENV=production
(you could disable debug too).
Use redis for everything if you want (but don't forget to include predis/predis
to your laravel project in that case)
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_DRIVER=redis
Generate the laravel key:
cd /home/myapp/www/current
sudo -u myapp php artisan key:generate
Run your first migration
sudo -u myapp php artisan migrate --force
Authorize www-data
to write in storage:
chown -R www-data:www-data /home/myapp/www/shared/storage
Nginx
Edit /etc/nginx/fastcgi.conf
and replace SCRIPT_FILENAME
and DOCUMENT_ROOT
with:
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
Then create and edit /etc/nginx/sites-available/myapp
server {
listen 80 default_server;
server_name _;
root /home/myapp/www/current/public;
server_tokens off;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
index index.html index.htm index.php;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_log /var/log/nginx/hello-error.log error;
error_page 404 /index.php;
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
fastcgi_index index.php;
include fastcgi.conf;
}
location ~ /\.ht {
deny all;
}
}
Disable default Nginx site and enable your myapp site:
rm /etc/nginx/sites-enabled/default
ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/myapp
Reload nginx
service nginx reload
You should be able to connect to your website with the IP adress.
Your user
Create and configure your user (type a password when prompted)
useradd raphael
adduser raphael sudo
chsh -s /bin/bash raphael
mkdir /home/raphael
chown -R raphael:raphael /home/raphael
cp /root/.profile /home/raphael/.profile
cp /root/.bashrc /home/raphael/.bashrc
passwd raphael
Go to your computer (not your server) to authorize your SSH key (replace ADDRESS
with your address and raphael
with your name)
ssh-copy-id raphael@ADDRESS
Test if connection works (you should not be prompted for password)
ssh raphael@ADDRESS
Now you should be connected with your account. Good bye root
, we will not use it anymore.
Disable password auth
Edit /etc/ssh/sshd_config
and add this
PasswordAuthentication no
Then run
sudo service ssh restart
Https
Everything is https now, here is how to have one for your domain (replace your.domain.com
)
sudo apt install letsencrypt
sudo service nginx stop
sudo letsencrypt certonly --standalone -d your.domain.com
Edit /etc/nginx/sites-available/myapp
and change the first lines
server {
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/your.domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your.domain.com/privkey.pem;
server_name _;
# ...
}
Enable automatic renew of the certificate
sudo letsencrypt renew --dry-run --agree-tos
sudo service nginx start
Edit /home/raphael/renew.sh
#!/bin/bash
service nginx stop
letsencrypt renew
service nginx start
Edit /etc/crontab
and add this
0 4 * * 0,3 root /home/raphael/renew.sh
Restart nginx
sudo service nginx start
Your website is ready. Go to https://your.domain.com and it works.