Home

WordPress on AWS Ubuntu 18.04 — full LAMP stack walkthrough

WordPress is still the most popular pick for static-ish content sites, and the LAMP stack is its classic home. This post is an end-to-end walkthrough — provision an AWS EC2 instance, install Apache, MySQL, PHP, phpMyAdmin, then drop WordPress on top and configure the database.

The plan: a free-tier t2.micro (1 vCPU, 1 GB RAM) on Ubuntu 18.04. Free tier gives you 750 hours/month — that's about one always-on box.

Heads up: Ubuntu 18.04 went end-of-standard-support in May 2023. For a fresh install today, prefer 22.04 or 24.04. The flow below is the same; the package versions just bump up.

1. Create the EC2 instance

Sign up for AWS at aws.amazon.com (free tier needs a credit card on file, but you won't be charged unless you go beyond the free tier limits).

Open the AWS console and click Launch a virtual machine.

Step 1. Pick Ubuntu Server 18.04 LTS.

Step 2. Pick t2.micro (the free-tier instance type).

Step 3. Leave the instance details at defaults.

Step 4. Disk — the default 8 GB is plenty for WordPress. You can go up to 1024 GB if you need more.

Step 5. Tag the instance (just a Name tag is enough).

Step 6. Configure the security group. Open the ports the box needs to be reachable on:

Step 7. Review and launch.

AWS prompts you to download the key pair. Save the .pem file — without it you can't SSH into the instance.

In a couple of minutes the instance is up. SSH in (any client works — ssh -i key.pem ubuntu@<public-ip> from a terminal, or a GUI client like Termius).

2. Install LAMP

Switch to root:

bash
sudo su

Refresh apt:

bash
apt-get update && apt-get upgrade -y

Install Apache:

bash
apt install apache2

Allow Apache through UFW:

bash
ufw app listufw allow in "Apache Full"

Confirm the public IP:

bash
ip addr show eth0 | grep inet | awk '{ print $2; }' | sed 's/\/.*$//'

Install MySQL:

bash
apt install mysql-server

Add the PPA for PHP 7.4 and install it:

bash
apt -y install software-properties-commonadd-apt-repository ppa:ondrej/phpapt-get updateapt -y install php7.4

PHP 7.4 hit end-of-life in November 2022. Use PHP 8.1 or 8.2 today.

Install phpMyAdmin (and a couple of PHP modules it needs):

bash
apt install phpmyadmin php-mbstring php-gettext

When the installer prompts for the web server, press Space to select Apache2, then OK.

Confirm:

Set a phpMyAdmin password:

Enable the mbstring PHP module and restart Apache:

bash
phpenmod mbstringsystemctl restart apache2

3. Configure MySQL

Drop into the MySQL shell as root:

bash
sudo mysql

List existing users to see what we're working with:

sql
SELECT user, authentication_string, plugin, host FROM mysql.user;

Out of the box on Ubuntu, MySQL's root uses auth_socket — only Linux root can log in. Switch it to a password:

sql
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'STRONG-PASSWORD-HERE';FLUSH PRIVILEGES;exit;

Re-enter as root:

bash
sudo mysql -u root -p

Create your own admin user (don't use root for app access in production):

sql
CREATE USER 'myadmin'@'localhost' IDENTIFIED BY 'STRONG-PASSWORD-HERE';GRANT ALL PRIVILEGES ON *.* TO 'myadmin'@'localhost' WITH GRANT OPTION;exit;

Install the PHP modules WordPress depends on, and restart Apache:

bash
apt install php-curl php-gd php-mbstring php-xml php-xmlrpc php-soap php-intl php-zipsystemctl restart apache2

4. Apache vhost for WordPress

Make a directory for the site:

bash
mkdir /var/www/wordpressdev

Edit the default vhost:

bash
cd /etc/apache2/sites-available/nano 000-default.conf

Use a config like this — point DocumentRoot at the new directory and allow .htaccess overrides:

apache
<VirtualHost *:80>    ServerAdmin webmaster@localhost    DocumentRoot /var/www/wordpressdev    ServerName wordpressdev    ServerAlias wordpressdev    <Directory /var/www/wordpressdev>        Options Indexes FollowSymLinks        AllowOverride All        Require all granted    </Directory>    ErrorLog ${APACHE_LOG_DIR}/error.log    CustomLog ${APACHE_LOG_DIR}/access.log combined    DirectoryIndex index.php index.pl index.cgi index.html index.xhtml index.htm</VirtualHost>

Enable mod_rewrite (WordPress permalinks need it), test, and reload:

bash
a2enmod rewriteapache2ctl configtestsystemctl restart apache2

5. Create the WordPress database

Open http://<server-ip>/phpmyadmin in your browser and log in with the MySQL admin user from step 3.

Create a new database for WordPress:

Grant the user privileges on it:

6. Install WordPress

Download the latest WordPress into /tmp and prepare the files:

bash
cd /tmpcurl -O https://wordpress.org/latest.tar.gztar xzvf latest.tar.gz# .htaccess (referenced by WP)touch /tmp/wordpress/.htaccess# wp-config from the samplecp /tmp/wordpress/wp-config-sample.php /tmp/wordpress/wp-config.php# upgrade dir WP needs at runtimemkdir /tmp/wordpress/wp-content/upgrade

Copy everything into the docroot:

bash
cp -a /tmp/wordpress/. /var/www/wordpressdevchown -R www-data:www-data /var/www/wordpressdevfind /var/www/wordpressdev/ -type d -exec chmod 750 {} \;find /var/www/wordpressdev/ -type f -exec chmod 640 {} \;

Generate a fresh set of WordPress keys:

bash
curl -s https://api.wordpress.org/secret-key/1.1/salt/

Output (yours will differ — this is just the shape):

php
define('AUTH_KEY',         'M4Flo3jnyOejU|lrQ?p]Gst^lZrK/<=qvwEey>i}|HMYl<RZZSLk6RjjjaN@Mz8l');define('SECURE_AUTH_KEY',  ';]1g,@v;xni^MZ+Gv`=>r4:N$b+!MJ(^&p[#M YluEKg031#{*8]gB]EGn{)JS-P');define('LOGGED_IN_KEY',    'bekiBS2vMgr^)+{J+{@QsQ0:d$`mw3B4x(mO1@r-aZ#Bx#rehuOOM>~FHorcaIk&');define('NONCE_KEY',        '00~EM=q+Rrt-o-oBvY55a#h`tFEi0.0, %]TQ[oA9A%%ElA<[ArbL`zFBZo>6EG=');define('AUTH_SALT',        '[k6ofHlfmUE?x^KIS!+-C;]Of:z|3&0I=m+^?Yz:+.1,tQoRm6LS$O-S0_SX|^y0');define('SECURE_AUTH_SALT', 'E<mRC&JN`m#?8$2Y0$me+w O=C9>n(|BVDM9nL+e+@k_6.7OmEAd<VLG~bY~9t-1');define('LOGGED_IN_SALT',   '?jAtR8gvw?Tj^v^!3tE.HYeMuP6q/r{4m)(Dg}(2P<2m>.n{gQ^+++_7>[O*LZ1[');define('NONCE_SALT',       '+E,QTp$?q!i+M9>ZFlw`5[{O*I?[$V6-H{/i1X.+hM3a6^5Lz%~p<!A=9<W4{JTA');

Edit wp-config.php:

bash
nano /var/www/wordpressdev/wp-config.php

Fill in the database name, user, and password from step 5, replace the placeholder keys with the ones from curl, and add define('FS_METHOD', 'direct'); at the bottom (so WP can install plugins/themes without prompting for FTP credentials).

The relevant bits look like:

php
// MySQLdefine('DB_NAME',     'wordpress');define('DB_USER',     'myadmin');define('DB_PASSWORD', 'STRONG-PASSWORD-HERE');define('DB_HOST',     'localhost');define('DB_CHARSET',  'utf8');define('DB_COLLATE',  '');// Auth keys (paste from curl above)define('AUTH_KEY',         '...');define('SECURE_AUTH_KEY',  '...');// ...$table_prefix = 'wp_';define('WP_DEBUG', false);if ( ! defined( 'ABSPATH' ) ) {    define( 'ABSPATH', dirname( __FILE__ ) . '/' );}require_once( ABSPATH . 'wp-settings.php' );define('FS_METHOD', 'direct');

7. Run the WordPress installer

Open http://<server-ip>/ in your browser. You should see the WordPress install screen:

Walk through the form, set up your admin user, and you're live.

What I'd add for production

This setup is fine for a personal site, but before exposing it to real traffic: