When you first create a new Ubuntu 22.04 server, you should perform some important configuration steps as part of the basic setup. These steps will increase the security and usability of your server, and will give you a solid foundation for subsequent actions.
See: https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-22-04
If you are not already connected to your server, log in now as the root user using the following command (substitute the highlighted portion of the command with your server’s public IP address):
ssh root@your_server_ipThis example creates a new user called acme:
adduser acmeAs root, run these commands to add your new user to the sudo and www-data groups:
usermod -aG sudo acme
# Also add the newly created user to www-data group
usermod -aG www-data acmeFirst, change the current user to acme:
su - acmeAs acme, run this command to generate a new SSH key on the server:
ssh-keygen -t rsa -b 4096Add the content from the public key file i.e. .pub to your github repository, settings > deploy keys > Add deploy keys:
cat ~/.ssh/id_rsa.pubSwitch back to the root user to complete the installation:
exit# Add PPA for NGINX Stable with HTTP/2
sudo add-apt-repository -y ppa:ondrej/nginx
sudo apt update
sudo apt -y install nginx
sudo systemctl enable nginx.serviceAs root, run these commands to enable the firewall and allow NGINX and OpenSSH ports only:
# List available apps
sudo ufw app list
# Allow 22, 80 and 443 ports only
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
# Start the firewall
sudo ufw --force enable
# Check the status
sudo ufw statussudo apt -y install fail2banOpen this file with your preferred text editor:
sudo nano /etc/fail2ban/jail.localPaste in the following configuration:
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1
# "bantime" is the number of seconds that a host is banned.
bantime = 6h
# A host is banned if it has generated "maxretry" during the last "findtime"
# seconds.
findtime = 30m
# "maxretry" is the number of failures before a host get banned.
maxretry = 3
[sshd]
enabled = true
[nginx-botsearch]
enabled = true
port = http,https
logpath = %(nginx_access_log)s
maxretry = 2
[nginx-limit-req]
enabled = true
port = http,https
logpath = %(nginx_error_log)s
maxretry = 2
bantime = 12hYou can test your configuration for syntax errors by typing:
sudo fail2ban-client -tIf any errors are reported, go back to your configuration file to review its contents before continuing.
When you are ready, restart Fail2ban service to enable the changes:
sudo systemctl restart fail2ban.serviceIf you plan on handling a large number of connections in a high performance environment, we recommend tuning the following kernel parameters:
sudo sysctl net.ipv4.tcp_syncookies=1
echo 'net.ipv4.tcp_syncookies=1' | sudo tee -a /etc/sysctl.conf
sudo sysctl vm.swappiness=1
echo 'vm.swappiness=1' | sudo tee -a /etc/sysctl.conf
sudo sysctl vm.vfs_cache_pressure=50
echo 'vm.vfs_cache_pressure=50' | sudo tee -a /etc/sysctl.conf
sudo sysctl vm.overcommit_memory=1
echo 'vm.overcommit_memory=1' | sudo tee -a /etc/sysctl.conf
sudo sysctl net.ipv4.tcp_max_syn_backlog=65535
echo 'net.ipv4.tcp_max_syn_backlog=65535' | sudo tee -a /etc/sysctl.conf
sudo sysctl net.core.somaxconn=65535
echo 'net.core.somaxconn=65535' | sudo tee -a /etc/sysctl.confThe LEMP software stack is a group of software that can be used to serve dynamic web pages and web applications written in PHP. This is an acronym that describes a Linux operating system, with an Nginx (pronounced like “Engine-X”) web server. The backend data is stored in the MySQL database and the dynamic processing is handled by PHP.
This guide demonstrates how to install a LEMP stack on an Ubuntu 22.04 server. The Ubuntu operating system takes care of the Linux portion of the stack. We will describe how to get the rest of the components up and running.
You have Nginx installed to serve your content. Now you can install PHP to process code and generate dynamic content for the web server.
# Add PPA for PHP
sudo add-apt-repository -y ppa:ondrej/php
sudo apt update
sudo apt -y install sudo apt -y install php8.2-bcmath php8.2-bz2 php8.2-cli php8.2-curl php8.2-fpm php8.2-gd php8.2-gmp php8.2-intl php8.2-mbstring php8.2-mysql php8.2-odbc php8.2-opcache php8.2-xml php8.2-zip php8.2-xslTo install Composer pacakge manager, run the following commands:
sudo curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
sudo chown acme:acme /usr/local/bin/composersudo apt -y install php8.2-imagickThe laravel-medialibrary package will use these tools to optimize converted images if they are present on our system.
Here's how to install all the optimizers on Ubuntu:
sudo apt -y install jpegoptim optipng pngquant gifsicle webpOpen this file with your preferred text editor
sudo nano /etc/php/8.2/fpm/pool.d/www.confPaste in the following configuration:
[www]
user = acme
group = acme
listen = /run/php/php8.2-fpm.sock
; listen = 127.0.0.1:9000
listen.owner = www-data
listen.group = www-data
;listen.mode = 0660
; listen.allowed_clients = 127.0.0.1
;listen.backlog = 511
listen.backlog = 4096
rlimit_files = 4096
pm = static
pm.max_children = 16
pm.max_requests = 256
pm.status_path = /status
;security.limit_extensions = .php .php3 .php4 .php5 .php7
security.limit_extensions = .php
php_admin_flag[expose_php] = off
php_flag[display_errors] = off
php_admin_flag[log_errors] = on
; php_admin_value[error_log] = /var/log/fpm-php.www.log
php_admin_value[log_level] = warning
php_admin_flag[session.cookie_secure] = on
php_admin_flag[session.cookie_httponly] = on
php_admin_value[memory_limit] = 256M
php_admin_value[upload_max_filesize] = 25M
php_admin_value[post_max_size] = 250M
php_admin_value[max_input_vars] = 1500
request_terminate_timeout = 60
php_admin_value[max_execution_time] = 60
php_admin_value[user_ini.filename] =
php_admin_value[opcache.memory_consumption] = 192
php_admin_value[opcache.interned_strings_buffer] = 16
php_admin_value[opcache.max_accelerated_files] = 30000
php_admin_flag[opcache.validate_timestamps] = off
php_admin_value[opcache.revalidate_freq] = 3
php_admin_flag[opcache.save_comments] = off
php_admin_value[realpath_cache_size] = 8192k
php_admin_value[disable_functions] = exec,passthru,shell_exec,system,proc_open,popen,parse_ini_file,show_source,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshareYou can test your configuration for syntax errors by typing:
sudo php-fpm8.2 -tIf any errors are reported, go back to your configuration file to review its contents before continuing.
When you are ready, restart PHP-FPM service to enable the changes:
sudo systemctl restart php8.2-fpm.serviceOn Ubuntu 22.04, Nginx has one server block enabled by default and is configured to serve documents out of a directory at /var/www/html.
Open the default file in Nginx’s sites-available directory using your preferred command-line editor. Here, we’ll use nano:
sudo nano /etc/nginx/sites-available/defaultPaste in the following bare-bones configuration:
server {
listen 80 default_server;
listen [::]:80 ipv6only=on default_server;
root /var/www/html;
server_name _;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
# Add index.php to the list if you are using PHP
index index.php index.html index.htm;
charset utf-8;
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
# try_files $uri $uri/ =404;
try_files $uri $uri/ /index.php;
}
# pass PHP scripts to FastCGI server
#
location ~ \.php$ {
include snippets/fastcgi-php.conf;
# With php-fpm (or other unix sockets):
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
# # With php-cgi (or other tcp sockets):
# fastcgi_pass 127.0.0.1:9000;
}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
location ~ /\.ht {
deny all;
}
}You can test your configuration for syntax errors by typing:
sudo nginx -tIf any errors are reported, go back to your configuration file to review its contents before continuing.
When you are ready, reload Nginx to apply the changes:
sudo nginx -s reloadsudo apt -y install mysql-server-8.0When the installation is finished, it’s recommended that you run a security script that comes pre-installed with MySQL. This script will remove some insecure default settings and lock down access to your database system. Start the interactive script by running:
# Improve the security of MySQL installation
sudo mysql_secure_installationOpen this file with your preferred text editor:
sudo nano /etc/mysql/conf.d/custom.cnfPaste in the following configuration:
[mysqld]
# GENERAL #
user = mysql
default-storage-engine = InnoDB
socket = /var/run/mysqld/mysqld.sock
pid-file = /var/run/mysqld/mysqld.pid
performance_schema = off
sql-mode = ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
# MyISAM #
key-buffer-size = 32M
# SAFETY #
max-allowed-packet = 16M
max-connect-errors = 1000000
# DATA STORAGE #
datadir = /var/lib/mysql/
# BINARY LOGGING #
skip-log-bin
# CACHES AND LIMITS #
tmp-table-size = 32M
max-heap-table-size = 32M
max-connections = 150
thread-cache-size = 50
open-files-limit = 65535
table-definition-cache = 1024
table-open-cache = 2048
skip-name-resolve = 1
# INNODB #
innodb-flush-method = O_DIRECT
innodb-log-files-in-group = 2
innodb-log-file-size = 16M
innodb-flush-log-at-trx-commit = 1
innodb-file-per-table = 1
innodb-buffer-pool-size = 128M
# Metadata statistic updates can impact strongly performance
innodb_stats_on_metadata = 0;
# LOGGING #
log-error = /var/log/mysql/error.log
log-queries-not-using-indexes = 0
slow-query-log = 0
slow-query-log-file = /var/log/mysql/slow.log
[mysqldump]
# Variable reference
# For MySQL 8.0: https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html
# For MariaDB: https://mariadb.com/kb/en/library/mysqldump/
quick
quote_names
max_allowed_packet = 64MRestart MySQL service to enable the changes:
sudo systemctl restart mysql.serviceIn Ubuntu systems running MySQL 5.7 (and later versions), the root MySQL user is set to authenticate using the auth_socket plugin by default rather than with a password. This allows for some greater security and usability in many cases, but it can also complicate things when you need to allow an external program (e.g., phpMyAdmin) to access the user.
In order to use a password to connect to MySQL as root, you will need to switch its authentication method from auth_socket to mysql_native_password. To do this, open up the MySQL prompt from your terminal:
sudo mysqlTo configure the root account to authenticate with a password, run the following ALTER USER command. Be sure to change password to a strong password of your choosing:
mysql> ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';Then, run FLUSH PRIVILEGES which tells the server to reload the grant tables and put your new changes into effect:
mysql> FLUSH PRIVILEGES;Redis is an open source, advanced key-value store. It is often referred to as a data structure server since keys can contain strings, hashes, lists, sets, and sorted sets.
# Add PPA for Redis
sudo add-apt-repository -y ppa:redislabs/redis
sudo apt update
sudo apt -y install redis
sudo systemctl enable redis-server.serviceBefore using Redis with Laravel, we need to install and use the PhpRedis PHP extension.
sudo apt -y install php8.1-redisFollow the steps on the Laravel documentation pages https://laravel.com/docs/9.x/redis#introduction https://laravel.com/docs/9.x/queues#driver-prerequisites
Open this file with your preferred text editor:
sudo nano /etc/redis/redis.confInside the file, find these directives and replace them with the following:
# Set the max number of connected clients at the same time. By default
maxclients 20000
# TCP listen() backlog.
# As the comment in redis.conf indicates, the value of `somaxconn` and `tcp_max_syn_backlog` may need to be increased at the OS level as well.
tcp-backlog 65535
# TCP keepalive.
tcp-keepalive 300
# Set a memory usage limit to the specified amount of bytes.
maxmemory 1gb
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory.
maxmemory-policy volatile-lru
# Save the DB to disk every 1 hour.
save 3600 1Under high load, occasional performance dips can occur due to memory allocation. This is something Salvatore, the creator of Redis, blogged about in the past. The performance issue is related to transparent hugepages, which you can disable at the OS level if needed.
echo 'never' > /sys/kernel/mm/transparent_hugepage/enabledSupervisor is a process monitor for the Linux operating system, and will automatically restart your queue:work process if it fails.
More information here.
sudo apt -y install supervisorCreate a new worker configuration file:
sudo nano /etc/supervisor/conf.d/laravel-worker.confUse the following example code:
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/staging/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=acme
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/staging/storage/logs/worker.log
stopwaitsecs=3600Once the configuration file has been created, you may update the Supervisor configuration and start the processes using the following commands:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start laravel-worker:*At this point, you have a solid foundation for your server. You can install any of the software you need on your server now.