The upcoming 3-yr renewal of website hosting plan on Hostgator
and the desire to learn AWS cloud made me thinking about self-hosting of my personal website http://astroman.org
gradually increased regular costs of its Hatchling shared plan
from $3.95/mo to $6.95/mo
+ the cost of the domain to $15/yr => total for a 3yr term is about $300. For a Positive SSL certificate
one has to pay $50/yr + upgrade to the next tier of shared hosting plans => total cost over 3yrs readily rises to $600.
prices are predictably lower. At present t2.nano EC2 instances
are priced at $0.0059/Hour without a long-term commitment and at $69/3yrs = $2.9/mo for a 3 yr dedicated instance. Standard 8GiB of EBS storage
go for $0.8/mo. Thus, ones beats even the most discounted pricing of Hostgator... except one has to do much more work!
Typical web hosting consists of a lot of static contents in a form of HTML pages, images, CSS + WordPress blog + e-mail. SSL support is a premium paid feature. Under the hood, web hosting implies:
- a lot of HTML/PDF/CSS/JPEG/PHP/etc in a folder on a Linux host
- a domain name with adequate DNS service
- Apache web server routing to the content
- PHP engine + MySQL database to run WordPress
- Postfix SMTP and Dovecot POP3 servers for e-mail
- CA-signed SSL certificate with a mechanism for certificate renewal
I aimed at replicating all those features on an EC2 instance and succeeded in about 2 weeks of working on it about 5 evenings a week.
t2.nano EC2 instance has only 500MB memory, which prohibits installation of WHM/cPanel
=> more manual work. Luckily, all other software runs on such box without a hitch on a chosen Amazon AMI Linux distribution. Provisioning of EC2 instance is fairly standard, except I got a discounted dedicated instance on AWS marketplace with $2.9/mo pricing, but only a 2yr commitment. An instance should have an associated Elastic IP address
, which is free for as long as an instance is running. Regardless of the domain hosting registrar, the DNS service could be provided by Amazon via Route 53 service
, which offers seamless integration with other AWS services and best possible access to your domain. A hosted zone costs extra $0.5/month and I decided to pay that.
Amazon Linux is based on RedHat and has standard tools like yum
available. However, be careful as the default versions of packages may need to be abandoned in favor of compatible versions, e.g. use httpd24
instead of httpd:
- Configure DNS, edit /etc/sysconfig/network set HOSTNAME=yourhostname.com and restart server "sudo reboot".
- Install Apache: "sudo yum install httpd24".
- Copy files to /var/www/html with the entry point file named index.html.
- Edit /etc/httpd/conf/httpd.conf and comment out "AddDefaultCharset UTF-8" unless you have a Unicode-compatible website.
- Start Apache and set the service to autostart "sudo service httpd start" and "sudo chkconfig httpd on".
The website should now be accessible at http://yourhostname.com.
A WordPress blog can either be installed at the website root or made available at a specific URL such as http://yourhostname.com/blog. It requires PHP engine and MySQL database. Using PHP56 avoids conflicts with other versions. I migrated WordPress from a different hosting.
- Install PHP and MySQL
- sudo yum install php56-mysqlnd php56-gd php56 php56-common
sudo yum install mysql-server mysql
- Start and enable autostart of "mysqld" service.
- Secure MySQL installation with "sudo mysql_secure_installation", set root password etc.
- Connect to MySQL from command line and create a database for WordPress.
- mysql -u root -p password
CREATE DATABASE wordpress;
CREATE USER wordpressuser@localhost IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON wordpress.* TO wordpressuser@localhost IDENTIFIED BY 'password';
- On old WordPress instance install Duplicator plugin and create the archives, then copy the archives to the relevant folder in the new hosting.
- Access installer.php and follow the prompts to hook up to MySQL database, unpack the archive and make selections.
- If the website address/folder changes at a subsequent time, make necessary changes to MySQL database.
- Consider using TRUEedit plugin, which prevents conversion of "--" and other symbols to something non-copy-pastable.
Free self-signed certificates
cannot be used for anything other then testing. SSL certificates signed by trusted CA were always a paid premium feature, but not anymore. A new company Let's Encrypt now provides free SSL certificates for anyone! The certificates are cross-signed by IdenTrust, whose Certificate Authority public key is already present in most major browsers/operating systems
. Steps to get the certificate and use it with Apache:
- Get Let's Encrypt project
- sudo yum install git
- sudo git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt
- Obtain a certificate. Amazon Linux AMI support is experimental, but --debug flag successfully forces installation of relevant dependencies.
- sudo -H /opt/letsencrypt/letsencrypt-auto certonly --standalone -d astroman.org --debug
- The resultant 3 certificate files are referenced for Apache in /etc/httpd/conf.d/ssl.conf file as:
- SSLCertificateFile /etc/letsencrypt/live/yourhostname.com/cert.pem
- SSLCertificateKeyFile /etc/letsencrypt/live/yourhostname.com/privkey.pem
- SSLCertificateChainFile /etc/letsencrypt/live/yourhostname.com/fullchain.pem
- Include a permanent redirect to HTTPS in Apache config file /etc/httpd/conf/httpd.conf
- <VirtualHost *:80>
- ServerName yourhostname.com:80
- Redirect permanent / https://yourhostname.com/
- Allow overrides in VirtualHost on port 443 (otherwise links to individual posts will display error 404)
- vi /etc/httpd/conf.d/ssl.conf
- <Directory /var/www/html/blog>
- DirectoryIndex index.php
- AllowOverride All
- Order allow,deny
- Allow from all
- Open 443 port on EC2 instance and restart Apache. All links on your website including the main page and WordPress will now by HTTPS.
- Set up automatic certificate renewal to run daily in root crontab and redirect output to a file to check the renewal command runs. Most of the times the renewal script will skip renewal as the certificate is not yet due - it will only renew once in 60 days.
- sudo crontab -e
- 30 2 * * * /home/ec2-user/renewal.sh
- renewal.sh file logs the date and attempts to renew the certificates without Apache restart and without updating dependencies
- sudo echo `date` >> /home/ec2-user/renew.log
- sudo /opt/letsencrypt/certbot-auto renew --webroot -w /var/www/html --no-bootstrap >> /home/ec2-user/renew.log