How to properly (and securely) setup a Laravel server for development and production
Hi everyone!
Here I'm going to write a simple guide for properly setting up a Laravel server for development and production purposes, so you will have a complete instance for remote work and correct services configuration, file and folder permissions and much more!
The dependencies are the following:
- A remote Debian on a virtual machine to use only for this project, that we will configure here together if you don't have a fresh one available to use, with at least 2 cores and 2GB of RAM, and 30GB disk space for the main OS and the dependencies and the project itself
- An internet connection
- A secondary PC so we can configure it remotely after the OS installation step is done and SSH remote access is working correctly
Step 1: Installing the OS
Create a virtual machine on the virtual machine management software of your likings (in this case I'm using KVM with Virt Manager under an Arch Linux host), download the latest Debian ISO from official source and insert into the virtual CD/ROM reader of your virtual machine.
After that, boot the virtual machine and set up as per your likings, making sure to remember the usernames and passwords provided to the installation script so we can use them later, other than configuring a static IP for the server itself so we can rely on that to properly setup the Laravel side of things.
Step 2: Enabling remote SSH
Login into the Debian machine and enable SSH remote access with the following command:
sudo systemctl enable sshd.service --now
Test it via remote connection with a secondary PC and make sure that it works as intended with this command:
ssh username@ip_address
If everything is set up correctly, let's proceed to the following steps!
Step 3: Configuring a static IP
We now need to configure our machine with a static IP, in my case 10.0.0.167, by logging in as root on the machine and editing the file /etc/network/interfaces
with your favourite text editor in the following way, by changing from dhcp
to static
on your iface
interface, and adding the following additional configuration (adapted to your use-case) to make it like so:
iface enp1s0 inet static
address 10.0.0.167
netmask 255.255.255.0
gateway 10.0.0.1
dns-nameservers 1.1.1.1 8.8.8.8
Save the file and restart the networking
service with this command:
systemctl restart networking
Now let's check if the network is correctly configured by running the ip addr
command and looking for our network card and if it's configured properly.
Then, if it all checks out, let's test the network if ping via IP and via DNS server previously configured is working properly with these two commands:
ping 1.1.1.1
ping www.google.com
If all it's good, let's go on!
Step 4: Installing project's dependencies
Once connected remotely via SSH with the previous steps instructions, we're now gonna install the very much needed dependencies for making it all work, in particular:
- Git, for project versioning
- ACL, for file and folder permissions properly working
- PHP, the language Laravel is implemented in
- Nginx, the web server
- MariaDB, one of the compatible databases for Laravel, and also the de-facto main fork of MySQL
- NodeJS and NPM for compiling and providing support for the frontend part of Laravel
- Composer, which is literally "the missing package manager for PHP" as its website suggests
Note: I suggest you NOT installing the 'sudo' package via apt, and use root user directly for that.
So, all this converts to the following command:
# For most of the packages, install those via this command
apt install php php-fpm composer nginx mariadb-client mariadb-server git acl
# For Laravel-specific PHP dependencies, install also those packages
apt install php-xml php-dom php-mysql
# For NodeJS and NPM, we're gonna add the latest LTS version via the official "nvm" (node version manager) (updates to the script can be found here: https://nodejs.org/en/download/package-manager)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
nvm install 20
Step 5: Creating the project
Now we're going to create and setup our project, in this guide we're calling it playground
.
Let's make a project folder under /srv/services
and give it to our low-privileged app user, in my case mrcz
, like so:
mkdir /srv/services
mkdir /srv/services/playground
chown -R mrcz:www-data /srv/services/playground
With that, let's switch to our app user with this command
su - mrcz
and create our project with this command
cd /srv/services/playground
composer create-project laravel/laravel .
You should now have an empty Laravel project!
Let's start safe by initializating a local Git repository with the following commands:
git init .
git add .
git commit -m "Initial project status"
(you can configure where and with which user to push to a remote repository later by yourself!)
Step 6: Configure the project and related services
Now that we have the project installed, let's configure it by editing the .env
file under these specific keys, and uncomment those if they are currently disabled with a comment:
APP_NAME=Playground
APP_URL=http://playground.goldmark.local
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=playground
DB_USERNAME=playground
DB_PASSWORD=forsurethatssafebruh123
After that, we need to configure Nginx to reply to our requests and redirect those to the Laravel application, and for that we need a new nginx block configured for the purpose, which we will put it here under /etc/nginx/sites-available/100-playground.conf
with the following content:
server {
listen 80;
listen [::]:80;
server_name playground.goldmark.local;
root /srv/services/playground/public;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
index 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_page 404 /index.php;
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_hide_header X-Powered-By;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
Then, enable the service:
ln -sf /etc/nginx/sites-available/100-playground.conf /etc/nginx/sites-enabled/100-playground.conf
and restart Nginx to activate it:
systemctl restart nginx
Now we're gonna configure our database by doing the following commands:
mysql -u root
MariaDB [(none)]> CREATE USER `playground` identified by 'forsurethatssafebruh123';
MariaDB [(none)]> CREATE DATABASE playground;
MariaDB [(none)]> GRANT ALL PRIVILEGES ON playground.* TO `playground`;
MariaDB [(none)]> FLUSH PRIVILEGES;
MariaDB [(none)]> exit
Then, we will go back to the project folder as our app user and run the database migrations with
php artisan migrate
If it all checks out, you should see some migrations running!
Step 7: Add record to local DNS or network DNS
If you noticed before, we used playground.goldmark.local
both on Nginx and Laravel app configuration, that's because Nginx needs to know the name of the service he is exposing to make it work, so we need to add on your local DNS or on your network DNS the "A record" that matches your IP to the name you used, in my case I added 10.0.0.167 playground.goldmark.local
to my local network DNS and now it works!
You can also use your local machine DNS service, or hosts
file that you can find in the /etc/hosts
path if you want :)
Step 8: Configuring the scheduler
One way to configure the Laravel scheduler to run background tasks is with a cronjob, which can be configured this way by logging in as your app user (mrcz
in this case) and running the following command:
crontab -e
and pasting this at the end of the file
* * * * * cd /srv/services/playground && php artisan schedule:run >> /dev/null 2>&1
This makes sure that task scheduler is correctly working for Laravel!
Step 9: Configuring proper permissions for the project's folder
If you tried connecting to the project, you would see that it's throwing file permissions errors all over the place, and that's because the project is currently being accessed and run by PHP-FPM and Nginx which are both running under www-data
user, and the current project owner and group owner are currently mrcz:mrcz
, so we need a way to set it up so that the group of the Laravel project has always www-data
-accessible files and folders for working properly.
Let's start by correcting the current status of the project and migrating from mrcz:mrcz
to mrcz:www-data
as follows:
cd /srv/services/playground
chown -R mrcz:www-data ./
chmod -R ug+rwx storage bootstrap/cache
And now, let's configure for future use a proper mask so it will always write folders and files with the mrcz:www-data
match!
su -
chmod -R g+s /srv/services/playground
setfacl -Rdm user:mrcz:rwx,group:www-data:rwx /srv/services/playground/
We should be good to go by now!
Step 10: Testing
If you now go to "https://playground.goldmark.local" with a browser, you should see the Welcome screen of Laravel ready to work on! 🔥
Step 11: Have fun!
You didn't see it coming, did you? 😜
Conclusions
So now you have a properly configured Laravel server ready to develop with!
For production uses, you should also make sure to install ufw
for firewall purposes but other than that, it should be ready to go!
Thanks for reading it to the end :)
I hope it helped someone as it always has helped me in my work and homelab projects!
If you fancy see what I'm working on, have a look at my GitHub or write me an email!