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:
- 22 — SSH (so you can connect)
- 80 — HTTP
- 443 — HTTPS
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:
sudo suRefresh apt:
apt-get update && apt-get upgrade -yInstall Apache:
apt install apache2Allow Apache through UFW:
ufw app listufw allow in "Apache Full"Confirm the public IP:
ip addr show eth0 | grep inet | awk '{ print $2; }' | sed 's/\/.*$//'Install MySQL:
apt install mysql-serverAdd the PPA for PHP 7.4 and install it:
apt -y install software-properties-commonadd-apt-repository ppa:ondrej/phpapt-get updateapt -y install php7.4PHP 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):
apt install phpmyadmin php-mbstring php-gettextWhen 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:
phpenmod mbstringsystemctl restart apache23. Configure MySQL
Drop into the MySQL shell as root:
sudo mysqlList existing users to see what we're working with:
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:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'STRONG-PASSWORD-HERE';FLUSH PRIVILEGES;exit;Re-enter as root:
sudo mysql -u root -pCreate your own admin user (don't use root for app access in production):
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:
apt install php-curl php-gd php-mbstring php-xml php-xmlrpc php-soap php-intl php-zipsystemctl restart apache24. Apache vhost for WordPress
Make a directory for the site:
mkdir /var/www/wordpressdevEdit the default vhost:
cd /etc/apache2/sites-available/nano 000-default.confUse a config like this — point DocumentRoot at the new directory and allow .htaccess overrides:
<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:
a2enmod rewriteapache2ctl configtestsystemctl restart apache25. 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:
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/upgradeCopy everything into the docroot:
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:
curl -s https://api.wordpress.org/secret-key/1.1/salt/Output (yours will differ — this is just the shape):
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:
nano /var/www/wordpressdev/wp-config.phpFill 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:
// 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:
- HTTPS: install certbot for a free Let's Encrypt cert (or with Apache's plugin instead of Nginx).
- Move MySQL off the box if you outgrow
t2.micro— RDS or Aurora. - Backups: db dumps + filesystem snapshots on a schedule.
- Stronger MySQL config: don't grant
*.*ALL PRIVILEGES to a single user; scope to the WP database. - Fail2ban + restricted SSH: change SSH off port 22, or better, gate it behind a bastion.
- CloudFront / a CDN: WordPress is content-heavy and benefits a lot from caching at the edge.