This tutorial shows how to install WordPress on Linux VPS example with Digital Ocean provider
VPS
- First, you buy a new VPS on https://www.digitalocean.com. Select smallest configuration $5/m is fine
- Remember with Digital Ocean: create a new droplet with login by username + password to void Proftp login 530 error
Install
Add user account
ssh root@SERVER_IP_ADDRESS
=> Change root password
adduser nhancv
adduser nhancv sudo
usermod -aG sudo nhancv
su - nhancv
exit
ssh nhancv@SERVER_IP_ADDRESS
- [Optional] For register SSL to git version control ex: Bitbucket, Github if you store source on there
mkdir ~/.ssh
ssh-keygen -t rsa
# copy ssh to register for git version system: cat ~/.ssh/id_rsa.pub
- Add authorized key to login via ssh key on client
nano ~/.ssh/authorized_keys
-> copy ~/.ssh/id_rsa.pub on mac client to vps server
exit
# connect to VPS again to get new applied config
- Config sudo without password
sudo visudo
=> Add to bottom of file
nhancv ALL=(ALL) NOPASSWD: ALL
Install Nginx
sudo apt update
sudo apt install nginx
sudo nano /etc/nginx/nginx.conf
# Increase client max body size by add below command to config file (in http block) and gzip compressor
http {
##
# Basic Settings
##
client_max_body_size 20M;
...
##
# Gzip Settings
##
gzip on;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
}
# Check config and restart nginx
sudo nginx -t -c /etc/nginx/nginx.conf
sudo service nginx restart
Install FTP
sudo apt install proftpd
# After install, can test connection with vps credentials
- Disable Default Root
sudo nano /etc/proftpd/proftpd.conf
UNCOMMENT at line
# Use this to jail all users in their homes
# DefaultRoot ~
=>
# Use this to jail all users in their homes
DefaultRoot
Install MySQL
sudo apt update
sudo apt install mysql-server
sudo mysql_secure_installation
-> Yes for all
- The secure config database account.
$ sudo mysql
mysql> ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'Root@123';
mysql> CREATE USER 'admin'@'%' IDENTIFIED BY 'Admin@123';
mysql> GRANT ALL PRIVILEGES ON *.* TO 'admin'@'%' WITH GRANT OPTION;
mysql> FLUSH PRIVILEGES;
mysql> exit
- Test connection
# Root
sudo mysql -u root -p
# Admin
sudo mysql -u admin -p
- Create a database for WordPress
$ sudo mysql -u admin -p
mysql> CREATE DATABASE wordpress_database_nc;
mysql> CREATE USER 'wordpress_user_sql'@'localhost' IDENTIFIED BY 'Admin@123';
mysql> GRANT ALL PRIVILEGES ON wordpress_database_nc.* TO 'wordpress_user_sql'@'localhost' IDENTIFIED BY 'Admin@123';
mysql> FLUSH PRIVILEGES;
mysql> exit
- Config allows remote access
$ sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf
# Change the line bind-address = 127.0.0.1 to bind-address = 0.0.0.0
bind-address = 0.0.0.0
# Restart mysql
sudo systemctl restart mysql
Install PHP 7.2, PHP version will display after install php-fpm
sudo apt install software-properties-common
sudo add-apt-repository ppa:ondrej/php
sudo apt update
sudo apt install curl unzip git php-fpm php-mysql php-gd php-curl php-xml php-zip php-mbstring php-bcmath php-bz2 php-imagick php-dom
# Config git
git config --global user.name "Nhan Cao"
git config --global user.email [email protected]
# Configure the PHP Processor
sudo nano /etc/php/7.2/fpm/php.ini
# Update cgi fix path
; http://php.net/cgi.fix-pathinfo
cgi.fix_pathinfo=0
# Update upload_max_filesize
; Maximum allowed size for uploaded files.
; http://php.net/upload-max-filesize
upload_max_filesize = 50M
; Maximum size of POST data that PHP will accept.
; Its value may be 0 to disable the limit. It is ignored if POST data reading
; is disabled through enable_post_data_reading.
; http://php.net/post-max-size
post_max_size = 20M
# Restart PHP Processor
sudo systemctl restart php7.2-fpm
Install WordPress
- Prepare workspace
mkdir -p ~/workspace/
cd ~/workspace/
- Download WordPress at https://wordpress.org/download/
wget https://wordpress.org/latest.zip -O ~/workspace/wplatest.zip
- Extract in Documents
unzip wplatest.zip
cp -r wordpress/ nhancv.com/
cd nhancv.com
mkdir wp-content/uploads
sudo chown www-data:www-data -R *
sudo find . -type d -exec chmod 755 {} \;
sudo find . -type f -exec chmod 644 {} \;
- Add FTP for WordPress user
sudo useradd wordpress_ftp
sudo passwd wordpress_ftp
sudo usermod -m -d /home/nhancv/workspace/nhancv.com wordpress_ftp
sudo setfacl -R -m u:wordpress_ftp:rwx /home/nhancv/workspace/nhancv.com
sudo service proftpd restart
- [Optional for dev] Declare virtual host IP in /etc/hosts on macOS client in Dev mode
sudo nano /etc/hosts
- For macOS client
171.16.30.31 nhancv.com
- For ubuntu server
127.0.0.1 nhancv.com
- Redirect default to nhancv.com (redirect in Nginx https://www.liquidweb.com/kb/redirecting-urls-using-nginx/)
sudo nano /etc/nginx/sites-available/default
# Content
server {
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
rewrite ^/(.*)$ https://nhancv.com/$1 permanent;
location / {
try_files $uri $uri/ =404;
}
listen 80 default_server;
listen [::]:80 default_server;
}
- Create new Nginx config for nhancv.com site
sudo nano /etc/nginx/sites-available/nhancv.com
- Nginx config for wordpress site
server {
listen 80;
listen [::]:80;
root /home/nhancv/workspace/nhancv.com;
index index.php index.html index.htm;
server_name nhancv.com *.nhancv.com;
# This order might seem weird - this is attempted to match last if rules below fail.
# http://wiki.nginx.org/HttpCoreModule
location / {
try_files $uri $uri/ /index.php?$args;
}
# Add trailing slash to */wp-admin requests.
rewrite /wp-admin$ $scheme://$host$uri/ permanent;
# Directives to send expires headers and turn off 404 error logging.
location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
access_log off; log_not_found off; expires max;
}
# Uncomment one of the lines below for the appropriate caching plugin (if used).
#include global/wordpress-wp-super-cache.conf;
#include global/wordpress-w3-total-cache.conf;
location ~ [^/]\.php(/|$) {
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
if (!-f $document_root$fastcgi_script_name) {
return 404;
}
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.2-fpm.sock;
}
location ~ /\.ht {
deny all;
}
}
# Make alias and apply
sudo ln -s /etc/nginx/sites-available/nhancv.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo service nginx reload
- Access to nhancv.com
- Complete setup guide on the web
- Create
wp-config.php
manually
nano /home/nhancv/workspace/nhancv.com/wp-config.php
sudo chown www-data:www-data /home/nhancv/workspace/nhancv.com/wp-config.php
- To backup database using MySQL dump
mysqldump -u wordpress_user_sql -p wordpress_database_nc > wordpress_database_nc_bk.sql
Setup SSL for nhancv.com domain
sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository universe
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install certbot python-certbot-nginx
sudo certbot --nginx
# or for create new certificates only
sudo certbot certonly —nginx
# View all certificates information
sudo certbot certificates
Merge wildcard domain to home
- [Optional] Delete old domain cert
sudo certbot delete --cert-name nhancv.com
sudo certbot certonly \
--server https://acme-v02.api.letsencrypt.org/directory \
--manual --preferred-challenges dns -d *.nhancv.com -d nhancv.com
-> Yes ->
Please deploy a DNS TXT record under the name
_acme-challenge.nhancv.com with the following value:
yB0AXXXXXXORZXTwzeXXXXXXXXXXXXXXXXmOoA1-XXX
Before continuing, verify the record is deployed.
==> Go to domain provider (ex: namecheap.com) -> Select domain -> Manage -> Advance DNS ->
+ Add new TXT Record type with
+ Host: _acme-challenge
+ Value: yB0AXXXXXXORZXTwzeXXXXXXXXXXXXXXXXmOoA1-XXX
+ Save -> It may take some minutes
To check new config applied, open new terminal:
$ nslookup -type=TXT _acme-challenge.nhancv.com
-> If you get `*** Can't find _acme-challenge.nhancv.com: No answer` mean the config is not successful. Response for successful:
Non-authoritative answer:
_acme-challenge.nhancv.com text = "yB0AXXXXXXORZXTwzeXXXXXXXXXXXXXXXXmOoA1-XXX"
When TXT config applied on domain provider, you need back to VPS terminal and enter to complete process. Successful message is
——>
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/nhancv.com-0001/fullchain.pem
- Update nginx domain with new key file and server_name to *.nhancv.com
- Now you can access to:
nhancv.com
www.nhancv.com
https://nhancv.com
https://www.nhancv.com

- Backup Nginx config:
/etc/nginx/sites-available/default
server {
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
rewrite ^/(.*)$ https://nhancv.com/$1 permanent;
location / {
try_files $uri $uri/ =404;
}
listen 80 default_server;
listen [::]:80 default_server;
}
- Backup Nginx config for
/etc/nginx/sites-available/nhancv.com
server {
root /home/nhancv/workspace/nhancv.com;
index index.php index.html index.htm;
server_name nhancv.com;
# This order might seem weird - this is attempted to match last if rules below fail.
# http://wiki.nginx.org/HttpCoreModule
location / {
try_files $uri $uri/ /index.php?$args;
}
# Add trailing slash to */wp-admin requests.
rewrite /wp-admin$ $scheme://$host$uri/ permanent;
# Directives to send expires headers and turn off 404 error logging.
location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
access_log off; log_not_found off; expires max;
}
# Uncomment one of the lines below for the appropriate caching plugin (if used).
#include global/wordpress-wp-super-cache.conf;
#include global/wordpress-w3-total-cache.conf;
location ~ [^/]\.php(/|$) {
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
if (!-f $document_root$fastcgi_script_name) {
return 404;
}
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.2-fpm.sock;
}
location ~ /\.ht {
deny all;
}
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/nhancv.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/nhancv.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
listen 80;
listen [::]:80;
server_name *.nhancv.com;
rewrite ^/(.*)$ https://nhancv.com/$1 permanent;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /etc/letsencrypt/live/nhancv.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/nhancv.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
server_name *.nhancv.com;
rewrite ^/(.*)$ https://nhancv.com/$1 permanent;
}