Warning: You are reading an outdated version of this guide. Unless you're here for a specific reason, you probably want the updated version instead.
This article takes you through setting up the server used to host and serve your web app. You might also benefit from Digital Ocean’s guide to setting up an Ubuntu server, which covers similar material.
To follow this guide, you’ll need SSH access to either a virtual private server (VPS) or a dedicated physical server to host your web app. You should also read through the conventions used in this guide if you haven’t yet.
# Log in to your server over SSH.
# Do this however you like, e.g. something along the lines of:
you@dev$ ssh [email protected] -i ~/path/to/your/ssh-key
I suggest first updating your package lists and upgrading your system packages:
# Update package lists and upgrade packages
root@server$ apt update
root@server$ apt upgrade
This is to ensure you’re starting off with up-to-date software.
Then create a non-root user to manage your web app.
I’ll create and use a non-root user called laravel
in this guide, but the name “laravel” is completely arbitrary. Feel to use a different name—but be sure to stay consistent throughout the guide.
# Create a non-root user
root@server$ adduser laravel
# Give the non-root user a password
root@server$ passwd laravel
# Give user sudo privileges
root@server$ usermod -aG sudo laravel
# Optional: change default shell to bash.
# (Otherwise you might be stuck with `sh` on some systems.)
root@server$ chsh -s /usr/bin/bash laravel
# Optional: you might want to also explicitly set the *root* user's password
root@server$ passwd
Best practice. The root user (likely the only user present on a freshly server) has privileges to access/delete/overwrite basically anything on the server without any prompts or confirmation, and can easily cause destructive changes—often by accident.
This is more firepower than you need for day-to-day tasks of managing and serving a web app, and it is best to delegate those tasks to a dedicated non-root user instead. You’ll both avoid potential root-related footguns and have a (IMO) more organized setup—one dedicated user per web app feels “cleaner” than just using blundering around as root for everything.
Note that you can still give the non-root user sudo
privileges to use for one-off tasks that require them; the point is not to perform everything as root.
From here forward we’ll work as the laravel
user.
Here’s how to change users:
# Change to the non-root user for the remainder of the guide
root@server$ su laravel
authorized_keys
fileTLDR: Register the public SSH key you use to access your server in the laravel
user’s authorized_keys
file on your server.
Then set appropriate SSH file permissions.
You can now jump to the next section.
End TLDR.
There are a few ways to do this—choose whichever you prefer:
Option 1: use ssh-copy-id
from your development machine (I’m assuming you’ve done this before).
Option 2: copy the root user’s authorized_keys
file.
Your cloud provider (e.g. Digital Ocean) may have already created an authorized_keys
file for the root user when provisioning your server.
In this case you can just copy the root user’s authorized_keys
file—but make sure to update permissions:
# Create an SSH directory for the laravel user, if needed
laravel@server:~$ mkdir .ssh
# Copy the root user's authorized_keys file, which should already have your
# public SSH key
laravel@server:~$ sudo cp /root/.ssh/authorized_keys .ssh/authorized_keys
# Make sure the laravel user owns their SSH directory and its contents!
laravel@server:~$ sudo chown -R laravel:laravel .ssh
Option 3: create an authorized_keys
and manually type out or paste the appropriate SSH key from your development machine.
# Manually open (or create) the laravel user's authorized_keys file and
# place the appropriate public SSH key inside
laravel@server:~$ nano .ssh/authorized_keys
In all three cases you’ll want to set correct SSH-related permissions:
# Change into laravel user's home directory
laravel@server$ cd ~
# Ensure laravel user owns their SSH directory and all files within
laravel@server$ sudo chown -R laravel:laravel .ssh
# Give the owning user (i.e. laravel) read, write, and execute permissions
# on their SSH directory
laravel@server$ chmod 700 .ssh
# Give the owning user (i.e. laravel) read and write permissions
laravel@server$ chmod 600 .ssh/authorized_keys
The permissions I’ve used for .ssh/
and authorized_keys
are standard best practice—see e.g. the FILES
section of man ssh
for an authoritative source.
First confirm you can log in to your server from your dev machine as the laravel
user over SSH using public-key authentication. (Otherwise you run the risk of locking yourself out of your server!)
Assuming you can indeed access your server as the laravel
user, you can now complete the two classic SSH hardening steps everyone (rightly) nags you about: open /etc/ssh/sshd_config
(you’ll need root privileges) and
The updated settings in your server-side sshd_config
should look like this:
# Both settings should be changed from "yes" to "no"
PasswordAuthentication no
PermitRootLogin no
These steps are standard SSH best practice—I’m assuming you’ve seen this a million times before and won’t go into long explanations. (New to SSH? I’d recommend this Digital Ocean SSH guide to learn the basics.)
If you disable password-based authentication, you (obviously) won’t be able to log in to your server over SSH using a user’s password.
This is why you should confirm you can access your server with public-key authentication before setting PasswordAuthentication no
.
If you disable root login, you won’t be able to log in to your server over SSH as the root user, even using public key authentication.
This is why you should confirm you can access your server as the laravel
user before setting PermitRootLogin no
.
You’ll be safe as long as you double check you can access your server from your dev machine as the non-root user using public key authentication:
# Confirm you can log in to your server as the `laravel` user over SSH using
# public-key authentication.
you@dev$ ssh [email protected] -i ~/path/to/your/ssh-key
Here’s an optional tip for easier SSH access to your server from your development machine: create an alias in the SSH config on your dev machine.
In ~/.ssh/config
on your development machine add:
# Update "laravel_project" to any nickname of your choice
Host laravel_project
# Update to your server's IP address
HostName 1.2.3.4
# This should be the non-root user on the server
User laravel
# Update to the full path to the private SSH key used to access your server
IdentityFile ~/.ssh/LaravelApp_id_ed25519
You can then use ssh laravel_project
instead of ssh [email protected]
to connect to the server from your dev machine.
Every Internet-facing server should have a firewall configured—yours is no exception.
I’m assuming most readers will have previous firewall experiences. But just in case…
For our purposes, a firewall is a program running on your server that filters incoming and outgoing Internet traffic, usually to secure your server (there are other uses, too). Setting up a firewall is a basic part of server administration.
We will use a firewall to improve your server’s security by restricting the Internet connections that can reach your server to a few well-known services (SSH, HTTP and HTTPS via the Nginx web server) and dropping all other incoming Internet traffic. The idea is to restrict the (all-too-often malicious) traffic that can reach your server from the public Internet.
A thorough discussion of firewalls falls beyond the scope of this article; you should first be familiar with the TCP/IP stack, then read Wikipedia’s article on firewalls. You might also benefit from Digital Ocean’s guide to setting up a firewall on an Ubuntu machine, which covers similar material to this article.
We’ll use UFW to manage firewalls.
You will very likely have UFW preinstalled on a new server (check with apt list ufw
or which ufw
), but can always install it with apt install ufw
.
I’d suggest first resetting your UFW configuration to a clean slate, so that we’re all starting with the same configuration:
# Reset UFW to clean slate: deny all incoming and allow all outgoing traffic
laravel@server$ sudo ufw default deny incoming
laravel@server$ sudo ufw default allow outgoing
I would suggest the following for a basic web application:
# Allow OpenSSH and Web traffic
laravel@server$ sudo ufw allow 22 # OpenSSH
laravel@server$ sudo ufw allow 80 # HTTP
laravel@server$ sudo ufw allow 443 # HTTPS
ufw default deny incoming
and don’t subsequently allow any incoming services, you’ll lock yourself out of your server.
Make sure you leave yourself at least one way to reach your server (e.g. allowing incoming OpenSSH traffic over port 22).
Finally, enable the firewall:
# Enable firewall
laravel@server$ sudo ufw enable
# If you're interested, check firewall status
laravel@server$ sudo ufw status
I imagine many readers will be using Digital Ocean, so I wanted to mention this (because it confused me when I was starting out!).
Digital Ocean provides a free service called Cloud Firewalls that you can easily apply to any Digital Ocean droplet (you do this from the “Networking” section of the admin panel on Digital Ocean’s website; here is a quickstart guide and here are full docs).
Digital Ocean’s Cloud Firewalls are separate from ufw
firewalls (Cloud Firewalls are more Internet-facing, i.e. incoming IP packets first hit the Digital Ocean firewall, then the ufw
firewall.)
I’d suggest setting up both on a new droplet.
Next: The next article covers the installation of PHP and the PHP extensions needed for Laravel to run.
Finding this tutorial series useful? Consider saying thank you!
The original writing and media in this series is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.