Skip to content

Repository files navigation

CloudPress

Built and documented by Ali Adil.

CloudPress is my AWS setup for a WordPress business website. I built it for Cedar & Tide Studio, a fictional interior-design business in British Columbia. The website shows its services and story; WordPress provides the tools to manage its content.

Start here: Every service and connection explained in easy words · Open the website preview

The problem I set out to solve

I used this small-business scenario: the whole website depends on one server, which is a rented computer. If it fails, customers cannot see the site. If photos are saved only on that computer, replacing it can lose the photos. Updating the site by hand also makes mistakes harder to track and recover from.

I wanted a setup that could share the work between two servers, keep the website's data separate, restrict access to passwords and the database, and follow the same checked steps for each release. This is a learning project for a fictional business, not a claim about a real client's website.

My solution and how it works

  1. A visitor opens the website through CloudFront, its public entry point with an encrypted browser connection.
  2. A load balancer chooses a healthy website server to answer the request.
  3. One of two EC2 servers runs WordPress and builds the page.
  4. RDS, the database, stores pages, users and settings. EFS, shared file storage, gives both servers access to the same upload folder.
  5. When I release a change, automated checks run first. The deployment then updates one server at a time and checks it before returning it to service.
  6. CloudWatch collects logs and measurements so I can inspect errors and the health of the system.

I wrote the AWS setup in Terraform, which is a set of files describing what AWS should create. That makes the setup repeatable and gives me a way to remove the temporary environment after testing.

This design reduces dependence on one website server. The short lab still used one database location and one shared outbound connection, so I do not claim that every part could survive a location failure. The service guide explains these limits without requiring AWS experience.

Where the parts sit: CloudFront is outside my VPC, the project's network. The load balancer is in public network sections. The website servers and database use private network sections. A visitor reaches the private servers through the load balancer; the visitor does not connect directly to the database. The connection walkthrough shows this with a diagram.

What I tested and what is available now

I deployed the WordPress application on AWS. Both website servers were healthy, all five release stages succeeded, and the homepage, services page, story page and health check passed. I did not run the separate server-failure, traffic-growth or backup-restore exercises. Test results and remaining exercises.

My account could not use CodeDeploy, the first deployment service I tried. I used Systems Manager to deliver the releases instead. The build journal records that problem and the other fixes.

I kept the lab short to control costs, then removed its AWS resources and checked the cleanup. The website preview remains on GitHub Pages. It shows the design and project story; it does not run WordPress or connect to a live AWS database. Cleanup evidence · My cost report.

Where to read next

  • Service guide: what every service did, why I used it, and an example request from start to finish.
  • Build journal: the problems I encountered and how I fixed them.
  • Testing: what I actually checked and what I have not tested.
  • Operations manual: the detailed steps for creating, updating and removing the AWS environment.

The sections below keep the technical settings and commands for someone who wants to rebuild the project.

Architecture

flowchart TB
  visitor[Visitor in British Columbia] -->|HTTPS| cf[CloudFront\nPresentation and cache policy]
  github[GitHub main branch] --> connection[CodeConnections]
  connection --> pipeline[CodePipeline\nBuild · initialize · deploy · verify]
  subgraph vpc[CloudPress VPC · us-east-1 · two Availability Zones]
    subgraph public[Public subnets]
      alb[Application Load Balancer\nCloudFront ranges + origin header]
      nat[NAT Gateway\none in lab / one per AZ in production]
    end
    subgraph application[Private application subnets]
      asg[EC2 Auto Scaling\nNginx + PHP-FPM + WordPress]
      init[Short-lived private CodeBuild\ndatabase initialization]
      efs[EFS regional filesystem\nshared uploads / TLS + IAM]
    end
    subgraph data[Private database subnets · no default internet route]
      rds[RDS MySQL 8.4\nprivate + encrypted + TLS required]
    end
    alb -->|HTTP inside VPC| asg
    asg -->|TLS / app DB user| rds
    asg -->|NFS over TLS| efs
    init -->|TLS / bootstrap DB admin| rds
    asg -->|outbound HTTPS| nat
    init -->|outbound HTTPS| nat
  end
  cf -->|HTTPS with custom domain / lab HTTP| alb
  pipeline --> init
  pipeline --> deploy[CodeBuild + Systems Manager\none instance at a time\noptional CodeDeploy]
  deploy --> asg
  secrets[Secrets Manager\nseparate master · app · initial admin] --> init
  secrets -->|app secret only| asg
  asg --> watch[CloudWatch logs, metrics, alarms]
  rds --> watch
Loading

The three tiers are presentation/web, application, and database. CloudFront/ALB handle public delivery and traffic distribution; PHP/WordPress implement application behavior; RDS stores persistent relational content. Nginx and PHP are deliberately colocated on each application host. This is a logical three-tier design, not a claim that every web/PHP process has a separate EC2 fleet. Detailed flows and failure boundaries.

Lab and production modes

Setting Short-lived lab production_mode=true
Initial app instances 2; SSM ASG range 2–3 2; ASG range 2–4
Database Single-AZ, 1-day backups Multi-AZ, 7-day backups
Outbound networking One NAT shared across AZs NAT in each AZ
Viewer URL Default CloudFront HTTPS hostname Required custom hostname/certificate
CloudFront → ALB HTTP, restricted source ranges and header HTTPS, certificate validated
ALB → application Private HTTP Private HTTP; see TLS boundary discussion
Deployment approval Automated after successful checks Manual production approval after build
Deletion protection Disposable lab settings RDS and ALB protection, final DB snapshot
Logs 7 days 30 days

Production mode enables specific infrastructure controls. It is not a claim of a fully audited production service or a guarantee of availability. Security controls and remaining limitations.

Deployment

The default enable_deployment=false creates no platform resources. Enabling it provisions the complete billable environment, rather than leaving an incomplete zero-instance demonstration.

Prerequisites:

  1. Existing working non-root AWS access and a verified account remaining on its Free plan.
  2. Terraform 1.10 or later; the recorded validation used 1.15.9.
  3. A GitHub repository and an AVAILABLE CodeConnections connection with access to that repository.
  4. A private input file with github_repository, connection_arn, expires_at, and explicit deployment opt-in.
  5. For production: an existing public Route 53 zone and the site's hostname. No domain purchase is automated.
terraform -chdir=terraform init
terraform -chdir=terraform plan -var-file=/private/cloudpress.tfvars -out=/private/cloudpress.tfplan
terraform -chdir=terraform apply /private/cloudpress.tfplan
terraform -chdir=terraform output -json platform

Use one protected state per environment. The repository includes a backend configuration example; it does not create or take ownership of an unrelated shared state bucket. The local state and plan files must remain private because the origin access header is stored in state. Database/admin passwords never pass through Terraform.

The first pipeline build downloads the pinned WordPress release, verifies its published core checksums, adds the original theme and platform plugin, and packages the release. A separate private CodeBuild stage creates the database-scoped application user and seeds the business website under a database lock. The selected delivery driver installs the same release on the ASG instances, validates dependencies, and controls ALB traffic. The default SSM driver verifies an immutable SHA256 archive, drains one host at a time, restores failed releases, and registers only locally healthy hosts. The last stage invalidates old CDN objects and checks the public website.

Full prerequisites, rollout, administrator access, local checks, troubleshooting, rollback, monitoring, and cleanup are in the operations manual.

Repository guide

Path Purpose
app/theme/ Original accessible Cedar & Tide WordPress theme; no third-party images or branding
app/mu-plugins/ Health endpoint and platform safeguards
app/wp-config.php Runtime-only secret/config loading and CMS restrictions
config/ Pinned application/tool versions and fictional business content
terraform/ Environment entry point and one cohesive reusable platform module, divided by responsibility
ops/ Initialization, atomic secret refresh, release verification/rollback, expiry guard, smoke checks
deploy/ Shared host lifecycle hooks, also usable by optional CodeDeploy
scripts/ Artifact build and intentional Terraform teardown helper
tests/ Credential handling, expiry, SQL scope/lock, release integrity, rollback, and response-contract tests
buildspec*.yml, appspec.yml The actual AWS-native delivery path
.github/workflows/quality.yml Additional pull-request checks; does not replace CodePipeline or hold AWS credentials

Validation and evidence

The live GitHub Pages preview preserves the original theme and links to this case study after the AWS lab. Its publication and browser check succeeded. It is explicitly a static portfolio preview with no active WordPress backend. Build and evidence details. The AWS pipeline remains the real application delivery path.

python -m unittest discover -s tests -v
python -m ops.security_scan
terraform -chdir=terraform fmt -check -recursive
terraform -chdir=terraform init -backend=false
terraform -chdir=terraform validate

I ran 40 local cases: 39 passed and one Windows symlink integration test was skipped. The Linux AWS quality build also passed. I then verified the real WordPress initialization, rolling deployment to two private servers, and all four public smoke-test paths. Every CodePipeline stage succeeded; both ALB targets were healthy. The journal explains the AWS CLI input failure and CloudFront hostname redirect I diagnosed and fixed. Live AWS results · Test scope and remaining exercises.

Security, cost, and design reasoning

  • Security and IAM: trust boundaries, secret lifecycle, ingress/egress, privilege limits, and production gaps.
  • Cost and cleanup: approximately $0.40–1.50 planned for a two-hour lab, a $2 troubleshooting allowance for three hours, and how to remove every project workload. I set a one-time $100 credit budget for all three projects and kept the demonstrations short.
  • Architecture decisions: why EC2/ASG, RDS, EFS, NAT, AWS-native delivery, and this region were chosen.
  • Build journal: actual difficulties, the account-plan blocker, corrections, and remaining evidence.
  • Interview preparation: concise explanations and technical questions with defensible answers.
  • Screenshot checklist: real captures only; no fabricated infrastructure or performance results.

The website intentionally uses fictional content and does not collect enquiries. This project favors a coherent operating system over a large service count. No measured performance, savings, production user count, or completed deployment claim should be inferred from the architecture alone.

Portfolio cost report

I consolidated my planning choices, AWS-reported charges and credit balance in my cost report. I keep estimates separate from posted billing and verify teardown independently.

About

WordPress platform on AWS: private EC2, RDS and EFS, Terraform, CodePipeline, and health-checked rolling deployment automation.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages