GithubHelp home page GithubHelp logo

collinbarrett / wp-vps-build-guide Goto Github PK

View Code? Open in Web Editor NEW
33.0 4.0 3.0 190 KB

A verbose build guide for a modern, high-performance WordPress production VPS.

License: MIT License

wordpress-site wordpress-vps wp-vps wordpress vps vps-admin digital-ocean nginx ubuntu wp-cli

wp-vps-build-guide's Introduction

wp-vps-build-guide

A verbose build guide for a modern, high-performance production WordPress VPS.

Introductory Blog Post

Intro

This project aims to provide a straightforward, albeit lengthy and all-inclusive, build guide for a low-budget, high-performance WordPress hosting solution. For as little as $5/mo., one can develop a cutting edge hosting stack for his or her projects. The instructions are verbose so that developers with little server administration experience can track.

The Stack

Component Solution Notes
Development Client macOS
Production Host DigitalOcean
Server Ubuntu LTS x64
WordPress Management Tools WP-CLI
Database MariaDB
Object Cache Store Redis in-RAM (persists)
PHP Compiler HHVM
Web Server NGINX w/FastCGI Caching in-RAM (persists)
Connection Modern TLS Ciphers
HTTP/2
ipv4/ipv6

Scope

This stack is designed to host one or multiple WordPress sites with light to medium loads. It will scale well, but it is not designed for an ultra-heavy use case that requires load balancing across multiple servers, etc. Server configurations are not one-size-fits-all, for sure, but hopefully this guide serves as a "good-enough-for-most" solution. While configuration recommendations provided are a good starting point, it is no substitution for ongoing optimization. Both speed and security have been key values during the development of this guide. The instructions to follow are scoped to only cover a single self-contained VPS. No load-balancing or CDN configuration is described, while these are highly recommended.

General Notes

  • Items in curly brackets {} should be treated as variables and replaced with custom info.
  • Recommended Snapshot points are annotated throughout, but feel free to take these more or less frequently.

Assumptions

  • The developer has basic Linux terminal skills.
  • The developer has access to a VPS host. DigitalOcean (DO) is used for the purposes of this guide, but competitors such as Linode work just fine with minor adaptations.
  • The developer has a ssh key already created. The public key is stored with the host and the private .pem stored locally at {myPK}.

Support

The best way to support this project is to submit issues and pull requests to assist in keeping the guide up-to-date. Clicking through the maintainer's DigitalOcean affiliate link when signing up is helpful as well, but by no means expected.

To amateurs at WordPress Operations...

Feel free to use this guide to turbocharge projects! Please submit issues or pull requests for any problems discovered.

To experts at WordPress Operations...

Please provide feedback. This guide should continue to receive ongoing optimizations and updates. In its current state, it will lead to a server that is higher-performing than most, but it is not perfect and the technologies powering it are constantly changing. Issues and pull requests are welcome.

Sources

This build guide is constructed from a compilation of sources from all over the web. Inline "via"s give credit to some of these authors, but apologies go out to any blogs that were forgotten. A special recognition goes out to Mark Jaquith and Carl Alexander whose talks played fundamental roles in this architecture.

Roadmap / To-Do

Near-Term

  • NGINX FastCGI Cache Tuning
  • HHVM Tuning
  • Redis Tuning
  • MariaDB Tuning
  • Verify Ubuntu Automatic Upgrades
  • Verify WordPress Ownership and Permissions
  • SSL Certificate Installation
  • SSH Key Installation
  • Automated Scheduled Backups

Moonshots / Long-Term

  • Automated Realtime Backups
  • Automated Build Script
  • Dockerize
  • Clusterize

Build Guide

  1. Create a new VPS running the latest Ubuntu LTS x64 in the DO control panel.
  • Enable backups.
  • Enable IPv6.
  • Add your SSH key.
  1. Locally, configure a ssh config file to make ssh easy.
  • In Terminal, sudo nano ~/.ssh/config

    Host {myVpsName}
      HostName {myVpsIP}
      Port 22
      User root
      IdentityFile {myPK}
    
    • Press "ctrl + x" to save and exit.
  1. ssh into the new VPS.
  • ssh {myVpsName}
    • Type "yes" to continue connecting.
  1. Create a new user and add it to the sudo group.
  • adduser {myUser}
    • Provide {myUserPassword} twice when prompted.
    • Press "return" repeatedly to accept the rest of the default options.
  • usermod -aG sudo {myUser}
  • via DigitalOcean
  1. Copy the ssh key to the new user and configure ssh.
  • mkdir /home/{myUser}/.ssh

  • cp ~/.ssh/authorized_keys /home/{myUser}/.ssh/

  • chown -R {myUser}:{myUser} /home/{myUser}/.ssh

  • chmod 700 /home/{myUser}/.ssh

  • chmod 600 /home/{myUser}/.ssh/authorized_keys

  • nano /etc/ssh/sshd_config

    • Modify PermitRootLogin no
    • Uncomment and modify PasswordAuthentication no
  • service ssh restart

  • Do not close the Terminal window yet. In a new Terminal window, sudo nano ~/.ssh/config

    Host {myVpsName}
      HostName {myVpsIP}
      Port 22
      User {myUser}
      IdentityFile {myPK}
    
  • Test ssh into the VPS as {myUser} before closing the root Terminal window.

    • ssh {myVPSName}
  • Type exit in the root Terminal window and close it.

  • via DigitalOcean

  1. Ensure all of the latest updates are installed.
  • sudo apt-get update
    • Provide {myUserPassword} when prompted.
  • sudo apt-get upgrade
  • sudo apt-get dist-upgrade
  1. Snapshot 1
  • sudo poweroff
  • Create a Snapshot in the DO control panel.
  1. Configure a basic firewall with ufw.
  • sudo ufw allow OpenSSH
  • sudo ufw enable
    • Type "y" to proceed with the operation.
  • via DigitalOcean
  1. Install fail2ban to protect SSH.
  • sudo apt-get install fail2ban
  • sudo service fail2ban restart
  • via DigitalOcean
  1. Update the timezone and configure ntp sync.
  • sudo dpkg-reconfigure tzdata
    • Select the local timezone.
  • sudo apt-get update
  • sudo apt-get install ntp
  • via DigitalOcean
  1. Enable a swap file.
  • sudo fallocate -l {swapSizeInGb}G /swapfile
    • For guidance on determining {swapSizeInGb}, see here.
  • sudo chmod 600 /swapfile
  • sudo mkswap /swapfile
  • sudo swapon /swapfile
  • sudo sh -c 'echo "/swapfile none swap sw 0 0" >> /etc/fstab'
  • via DigitalOcean
  1. Configure automatic updates, upgrades, and cleanup.
  • sudo nano /etc/apt/apt.conf.d/50unattended-upgrades
    • Uncomment "${distro_id}:${distro_codename}-updates";
  • sudo nano /etc/apt/apt.conf.d/10periodic
    • Modify APT::Periodic::Download-Upgradeable-Packages "1";
    • Modify APT::Periodic::AutocleanInterval "7";
  • via Ubuntu
  1. Snapshot 2
  2. Install NGINX with ngx_cache_purge.
  • sudo apt-get install nginx
  1. Install MariaDB.
  • Follow the 5 commands here based on the setup.
    • Use the DO node that the VPS is hosted on as the mirror in both the 4th box and the 3rd command.
    • Provide {myMariaDBRootPassword} twice when prompted.
  • mysql_secure_installation
    • Provide {myMariaDBRootPassword}.
    • Type n for do not change root password.
    • Press "return" repeatedly to accept the rest of the default options.
  1. Install HHVM.
  • sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0x5a16e7281be7a449
  • sudo add-apt-repository "deb http://dl.hhvm.com/ubuntu $(lsb_release -sc) main"
  • sudo apt-get update
  • sudo apt-get install hhvm
  • sudo update-rc.d hhvm defaults
  • sudo /usr/share/hhvm/install_fastcgi.sh
  • sudo mkdir /var/cache/hhvm (do only if RAM < 512mb)
  • sudo chown www-data:www-data /var/cache/hhvm/ (do only if RAM < 512mb)
  • sudo nano /etc/hhvm/server.ini
    • Replace hhvm.server.port = 9000 with hhvm.server.file_socket=/var/run/hhvm/hhvm.sock
    • Modify hhvm.repo.central.path = /var/cache/hhvm/hhvm.hhbc (do only if RAM < 512mb)
  • sudo service hhvm restart
  • via DigitalOcean
  1. Snapshot 3
  2. Create a database for WordPress.
  • mysql -u root -p
    • Provide {myMariaDBRootPassword}.
    • CREATE DATABASE {myWPDB} DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
    • GRANT ALL ON {myWPDB}.* TO '{myWPDBUser}'@'localhost' IDENTIFIED BY '{myWPDBPassword}';
    • FLUSH PRIVILEGES;
    • exit
  • Repeat this step for each WordPress site to be installed with new values for {myWPDB}, {myWPDBUser}, and {myWPDBPassword}.
  • via DigitalOcean
  1. Download and install WordPress.
  • wget http://wordpress.org/latest.tar.gz
  • tar -xzvf latest.tar.gz
  • rm latest.tar.gz
  • cd ~/wordpress
  • cp wp-config-sample.php wp-config.php
  • sudo nano wp-config.php
    • Modify define('DB_NAME', '{myWPDB}');
    • Modify define('DB_USER', '{myWPDBUser}');
    • Modify define('DB_PASSWORD', '{myWPDBPassword}');
    • Replace {myWPSecurityKeys} Generate {myWPSecurityKeys}
    • Modify $table_prefix = '{myRandomPrefix}_'; (Generate {myRandomPrefix})
    • Add define( 'WP_AUTO_UPDATE_CORE', true );
  • mkdir wp-content/uploads
  • sudo mkdir -p /var/www/{myWPSiteName}
  • sudo rsync -avP ~/wordpress/ /var/www/{myWPSiteName}/
  • rm -rf ~/wordpress/
  • Repeat this step for each WordPress site to be installed with new values for {myWPDB}, {myWPDBUser}, {myWPDBPassword}, {myWPSecurityKeys}, and {myRandomPrefix}.
  • via DigitalOcean
  1. Configure permissions and ownership. (WARNING: still under review, however these should be appropriate)
  • sudo chown root:root /var/www/{myWPSiteName}/
  • sudo chown -R {myUser}:{myUser} /var/www/{myWPSiteName}/*
  • sudo chown {myUser}:www-data /var/www/{myWPSiteName}/wp-config.php
  • sudo find /var/www/{myWPSiteName}/ -type d -exec chmod 755 {} \;
  • sudo find /var/www/{myWPSiteName}/ -type f -exec chmod 644 {} \;
  • sudo chmod 440 /var/www/{myWPSiteName}/wp-config.php
  • High Security Variant (does not allow plugins to be installed via Dashboard, recommended for use in conjunction with wp-cli)
    • sudo chown -R www-data:www-data /var/www/{myWPSiteName}/wp-content/uploads/
  • Medium Security Variant
    • sudo chown -R www-data:www-data /var/www/{myWPSiteName}/wp-content/
  1. Snapshot 4
  2. Configure FastCGI Cache RAM disk.
  • sudo mkdir /mnt/ramdisk
  • sudo nano /etc/fstab
    • Add tmpfs /mnt/ramdisk tmpfs defaults,size=32M 0 0
  • sudo mount /mnt/ramdisk
  • sudo mkdir /var/ramdisk-backup
  • sudo wget https://raw.githubusercontent.com/collinbarrett/wp-vps-build-guide/master/ramdisk -O /etc/init.d/ramdisk
  • sudo chmod +x /etc/init.d/ramdisk
  • sudo /etc/init.d/ramdisk sync
  • sudo crontab -e
    • Add @reboot /etc/init.d/ramdisk start >> /dev/null 2>&1
    • Add 2 * * * * /etc/init.d/ramdisk sync >> /dev/null 2>&1
  • via Observium
  1. Configure nginx.
  • sudo ufw allow 'Nginx Full'
  • sudo wget https://raw.githubusercontent.com/h5bp/server-configs-nginx/master/mime.types -O /etc/nginx/mime.types
  • sudo wget https://raw.githubusercontent.com/collinbarrett/wp-vps-build-guide/master/nginx.conf -O /etc/nginx/nginx.conf
  • sudo mkdir /etc/nginx/global
  • sudo wget https://raw.githubusercontent.com/collinbarrett/wp-vps-build-guide/master/global/common.conf -O /etc/nginx/global/common.conf
  • sudo wget https://raw.githubusercontent.com/collinbarrett/wp-vps-build-guide/master/global/wordpress.conf -O /etc/nginx/global/wordpress.conf
  • sudo wget https://raw.githubusercontent.com/collinbarrett/wp-vps-build-guide/master/global/hackrepair.conf -O /etc/nginx/global/hackrepair.conf
  • sudo rm /etc/nginx/sites-available/default
  • sudo rm /etc/nginx/sites-enabled/default
  • Note: Before using the example conf in the next step, you may need to edit it if you are not installing SSL (see step 24 and this issue).
  • sudo wget https://raw.githubusercontent.com/collinbarrett/wp-vps-build-guide/master/sites-available/example -O /etc/nginx/sites-available/example
  • sudo mv /etc/nginx/sites-available/example /etc/nginx/sites-available/{myWPSiteName}
  • sudo nano /etc/nginx/sites-available/{myWPSiteName}
    • Modify root /var/www/{myWPSiteName};
    • Replace example.com with {myWPSiteUrl}
    • If site should not be the default for the server, toggle listen directives so the ones without default_server are active.
  • sudo ln -s /etc/nginx/sites-available/{myWPSiteName} /etc/nginx/sites-enabled/{myWPSiteName}
  • Repeat the last four top-level bullets for each WordPress site to be installed with new values for {myWPSiteName} and {myWPSiteUrl}.
  • via DigitalOcean, DigitalOcean
  1. Configure TLS encryption.
  • sudo mkdir /etc/nginx/cert
  • sudo chmod 700 /etc/nginx/cert
  • sudo openssl dhparam 2048 -out /etc/nginx/cert/dhparam.pem
  • sudo chmod 600 /etc/nginx/cert/dhparam.pem
  • Install certificate(s) and key(s) to /etc/nginx/cert/.
  1. Snapshot 5
  2. Install and configure WP-CLI to auto-update WordPress.
  • curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
  • chmod +x wp-cli.phar
  • sudo mv wp-cli.phar /usr/local/bin/wp
  • sudo crontab -e
    • Add 0 1 * * * /usr/local/bin/wp cli update --yes --allow-root
  • crontab -e
    • Add 0 */6 * * * cd /var/www/{myWPSiteName}/ && /usr/local/bin/wp core update --quiet && /usr/local/bin/wp core update-db --quiet && /usr/local/bin/wp plugin update --all --quiet && /usr/local/bin/wp db optimize
  • via WP-CLI
  1. Install and configure Redis.
  • sudo apt-get install redis-server

  • sudo nano /etc/redis/redis.conf

    • Add maxmemory 64mb
    • Add maxmemory-policy allkeys-lru
  • sudo nano /var/www/{myWPSiteName}/wp-config.php

    define( 'WP_CACHE_KEY_SALT', '{myWPSiteName}_' );
    $redis_server = array( 'host' => '127.0.0.1', 'port' => 6379, );
    
  • cd /var/www/{myWPSiteName}/

  • wp plugin install wp-redis

  • sudo ln -s /var/www/{myWPSiteName}/wp-content/plugins/wp-redis/object-cache.php /var/www/{myWPSiteName}/wp-content

  • Verify Redis is working by redis-cli monitor and watching Terminal as you load {myWPSiteUrl} in a browser.

  • Repeat all but the first bullet for each WordPress site to be installed.

  • via Codeable

  1. Install and configure NGINX Helper.
  • cd /var/www/{myWPSiteName}/
  • sudo nano wp-config.php
    • Add define('RT_WP_NGINX_HELPER_CACHE_PATH','/mnt/ramdisk/nginx-cache');
  • wp plugin install nginx-helper --activate
  • Log into WordPress and navigate to "Settings -> Nginx Helper".
  • Configure settings as follows. Some settings do not appear until after you click "Save All Changes" the first time.
    • Check Enable Purge
    • Check nginx Fastcgi cache
    • Check Delete local server cache files
    • Check all Purging Conditions
  • Repeat all for each WordPress site to be installed.
  1. Snapshot 6

Ongoing Maintenance

  • If the VPS is ever resized, the swap file size should be re-evaluated.
  • The size of /mnt/ramdisk should be tuned on occasion.
  • MariaDB should be tuned on occasion.

wp-vps-build-guide's People

Contributors

collinbarrett avatar jbradach avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

wp-vps-build-guide's Issues

Command Not found

Hi After Installing HHVM
I run This Command sudo /usr/share/hhvm/install_fastcgi.sh
But it showing me command not found
How to solve This

Error when compiling nginx with custom OpenSSL

In step 13, where there is the current TODO regarding OpenSSL, I am trying the following...

  • cd /opt/nginx/nginx-1.9.3/debian/modules/
  • sudo wget https://www.openssl.org/source/openssl-1.0.2d.tar.gz
  • sudo tar -xzvf openssl-1.0.2d.tar.gz
  • sudo rm openssl-1.0.2d.tar.gz
  • sudo nano /opt/nginx/nginx-{npsCurVer}/debian/rules
    • Under "light" version flags:
      • Delete --without-ngx_http_limit_req_module \
      • Add \ to the end of the last flag.
      • Add --with-http_spdy_module \
      • Add --add-module=$(MODULESDIR)/nginx-cache-purge \
      • Add --add-module=$(MODULESDIR)/ngx_pagespeed-1.9.32.6-beta \
      • Add --with-openssl=$(MODULESDIR)/openssl-1.0.2d
  • cd /opt/nginx/nginx-1.9.3
  • sudo dpkg-buildpackage -b

The build mostly completes fine. It builds for a few minutes, seemingly compiling OpenSSL fine along with it. When I build it the exact same way without the custom OpenSSL module, it builds fine using the system installed version of OpenSSL. However, with the custom OpenSSL, it eventually errors out with the following error. I have also tried the same process with several other recent releases of OpenSSL with no success.

-Wl,-Bsymbolic-functions -fPIE -pie -Wl,-z,relro -Wl,-z,now -lpthread -lcrypt -lstdc++ /opt/nginx/nginx-1.9.3/debian/modules/ngx_pagespeed-1.9.32.6-beta/psol/lib/Debug/linux/x64/pagespeed_automatic.a -lrt -pthread -lm -lpcre /opt/nginx/nginx-1.9.3/debian/modules/openssl-1.0.2d/.openssl/lib/libssl.a /opt/nginx/nginx-1.9.3/debian/modules/openssl-1.0.2d/.openssl/lib/libcrypto.a -ldl -lz
/usr/bin/ld: /opt/nginx/nginx-1.9.3/debian/modules/openssl-1.0.2d/.openssl/lib/libssl.a(s23_meth.o): relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
/opt/nginx/nginx-1.9.3/debian/modules/openssl-1.0.2d/.openssl/lib/libssl.a: error adding symbols: Bad value
collect2: error: ld returned 1 exit status
objs/Makefile:272: recipe for target 'objs/nginx' failed
make[3]: *** [objs/nginx] Error 1
make[3]: Leaving directory '/opt/nginx/nginx-1.9.3/debian/build-light'
Makefile:8: recipe for target 'build' failed
make[2]: *** [build] Error 2
make[2]: Leaving directory '/opt/nginx/nginx-1.9.3/debian/build-light'
debian/rules:146: recipe for target 'build.arch.light' failed
make[1]: *** [build.arch.light] Error 2
make[1]: Leaving directory '/opt/nginx/nginx-1.9.3'
debian/rules:128: recipe for target 'build' failed
make: *** [build] Error 2
dpkg-buildpackage: error: debian/rules build gave error exit status 2

Reconsider blocking port 80

For usability, you may not want to block port 80 here. Just blocking it would prevent users from getting to the site if they typed the domain into the address bar without remembering to include the https://. A more user-friendly solution could be to add a server block listening on 80 that returns a 301 to the https version of the requested page.

Happy to submit as a pull request if you want to go this route.

Dockerize

I'd like to implement dockerization into this guide in the future, but I'm still researching the pros, cons, and best practices.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.