Running an e-commerce platform on a single public virtual machine is an operational liability waiting to surface. In this guide, you will learn how to design, provision, and deploy a containerized PrestaShop e-commerce store on Amazon Elastic Kubernetes Service (EKS) across multiple Availability Zones, eliminate public compute and database exposure, and inject production database credentials into application pods without writing a single plaintext secret to disk.
This project is the fourth installation in my cloud engineering series documenting hands-on enterprise infrastructure deployments. If you have followed my previous projects, such as building an automated deployment pipeline for a 3-tier application to eliminate configuration drift or designing an automated IAM governance system that onboards and offboards users in under 60 seconds, you know that my focus is on building hardened, production-grade cloud systems. You can follow my journey on Dev.to and track all project codebases on my GitHub profile.
The Architectural Evolution: Moving Past the Monolith
Prior to this project, I deployed PrestaShop using a conventional monolithic architecture: a single Amazon EC2 instance provisioned in a public subnet, configured manually with Apache and PHP, connected to a single-AZ Amazon RDS instance.
While that monolithic deployment served its purpose as an initial proof of concept, it carried severe operational and security flaws that no business handling financial transactions should tolerate:
- Direct Public Attack Surface: The application server sat in a public subnet with an open SSH port (22) exposed to the entire internet, inviting brute-force attacks and credential scans.
- Single Point of Failure (SPOF): Both the compute layer and the database ran in a single Availability Zone. An outage in an AWS data center would bring the entire storefront down immediately.
- Configuration Drift: Software packages, Apache virtual hosts, and PHP modules were installed manually through ad-hoc package manager commands. Replicating that server identically in an incident recovery scenario would have been slow and error-prone.
- Credential Exposure: Database credentials lived in plaintext PHP configuration files on the local filesystem, vulnerable to server compromises or unauthorized access.
To resolve these risks, I redesigned the platform from the ground up around containerization, Kubernetes orchestration, zero-trust network boundaries, and automated secret delivery.
Below is the live application preview deployed through an AWS Network Load Balancer (NLB):
-
Live Storefront Preview:
http://aad1534e7a20940148d569b875b1b9cd-82489033c55b4bb3.elb.us-east-1.amazonaws.com/
Note: If you are reading this post more than five hours after publication, this live endpoint has likely been decommissioned to prevent unnecessary cloud credit consumption. You can inspect the entire repository, configuration manifests, and implementation evidence on my GitHub repository.
High-Level Architecture Overview
The target architecture enforces complete separation of concerns and defense-in-depth across the following layers:
-
Multi-AZ Network Foundation (VPC): A dedicated Virtual Private Cloud spanning two Availability Zones (
us-east-1aandus-east-1c) divided into six subnets: two Public Subnets (hosting the Network Load Balancer and NAT Gateways), two Private Application Subnets (hosting the EKS worker nodes), and two Private Data Subnets (hosting Amazon RDS MySQL). - Zero-Trust Administrative Access: Rather than provisioning a public bastion jump host or opening port 22 to the internet, secure administrative access and database initialization are conducted through an EC2 Instance Connect Endpoint (EICE) residing inside the private subnet.
- Automated Database Failover: Amazon RDS MySQL deployed in a Multi-AZ standby configuration across the private data subnets, completely unreachable from the public internet.
- Containerized Packaging: PrestaShop packaged into a hardened Docker image with customized PHP memory, upload, and execution parameters, hosted in Amazon Elastic Container Registry (ECR).
- Kubernetes Orchestration (Amazon EKS): An Amazon EKS cluster orchestrating container scheduling across private worker nodes.
- In-Memory Secret Delivery: Integration of AWS Secrets Manager with Kubernetes through IAM Roles for Service Accounts (IRSA) and the Secrets Store CSI Driver, mounting database credentials directly into the container as files and memory-backed environment variables without persisting them in cluster storage.
- Traffic Distribution: An AWS Network Load Balancer (NLB) accepting external client requests and distributing traffic across healthy pods in the private subnets.
What You Will Need
Before starting, ensure you have the following accounts, permissions, and command-line tools ready:
- An active AWS Account with administrative permissions for VPC, EC2, RDS, IAM, ECR, EKS, and Secrets Manager.
-
AWS CLI (v2) installed and authenticated (
aws login). - Docker installed and running on your local machine (or an EC2 build environment).
- kubectl (Kubernetes command-line client) matching your target cluster version (v1.30 or v1.31).
- eksctl (the official CLI for Amazon EKS).
- Helm (v3) for package management on Kubernetes.
- A terminal shell (Bash or Zsh).
Step 1: Provisioning the Multi-AZ Network Topology
The foundation of a secure cloud architecture is strict network segmentation. Compute and database resources must never share subnets with public-facing ingress points.
Subnet Layout and IP Allocation
Create a custom VPC (10.0.0.0/16) with the following subnet CIDR blocks:
-
public-subnet-1(10.0.1.0/24) inus-east-1a -
public-subnet-2(10.0.2.0/24) inus-east-1c -
private-app-subnet-1(10.0.10.0/24) inus-east-1a -
private-app-subnet-2(10.0.11.0/24) inus-east-1c -
private-data-subnet-1(10.0.20.0/24) inus-east-1a -
private-data-subnet-2(10.0.21.0/24) inus-east-1c
Routing Rules and Gateway Integration
- Attach an Internet Gateway (IGW) to the VPC.
- Route
0.0.0.0/0in the Public Route Table directly to the Internet Gateway. - Deploy an AWS NAT Gateway inside
public-subnet-1and associate it with an Elastic IP. - Route
0.0.0.0/0in the Private Route Table to the NAT Gateway. Associate both private application subnets with this route table so EKS worker nodes can download system updates and container images without accepting inbound traffic. - Create an EC2 Instance Connect Endpoint (EICE) inside
private-app-subnet-1. This enables zero-trust private SSH tunneling into private instances directly through AWS IAM credentials without needing public IP addresses or internet gateways.
Step 2: Deploying Multi-AZ Amazon RDS and Storing Secrets
"Architecturally, the enterprise production blueprint specifies an Amazon RDS Multi-AZ deployment with synchronous standby replication across us-east-1a and us-east-1c for automated failover. However, to respect AWS Free Tier financial guardrails and prevent billing surprises during our development phase, I deployed a Single-AZ instance while establishing the DB Subnet Group across multiple availability zones. This delivers the exact network foundation required so that promoting the database to Multi-AZ for production requires zero architectural rework."
PrestaShop requires a reliable relational database backend. Instead of hosting MySQL directly inside a pod with volatile local storage, we delegate the persistence layer to managed Amazon RDS.
Creating the Multi-AZ Database (below are still the steps that would help you provision a multi-AZ DB if you have a paid account)
- Create a DB Subnet Group containing
private-data-subnet-1andprivate-data-subnet-2. - Provision an Amazon RDS MySQL instance (version 8.0) using the Multi-AZ deployment option. This provisions a primary database in one Availability Zone and an active-standby replica in the second zone, handling synchronous replication and automated failover.
- Configure the database security group (
rds-db-sg) to reject all inbound traffic by default.
Storing Credentials in AWS Secrets Manager
Create a secret named prestashop/db/credentials in AWS Secrets Manager:
aws secretsmanager create-secret \
--name prestashop/db/credentials \
--description "Database credentials for PrestaShop" \
--secret-string '{"username":"admin","password":"YourStrongSecretPassword123!"}' \
--region us-east-1
By storing credentials in AWS Secrets Manager, we establish a single source of truth for sensitive values. We will authorize Kubernetes pods to read this secret dynamically at runtime.
Step 3: Containerizing PrestaShop and Pushing to Amazon ECR
The official PrestaShop image requires specific PHP runtime adjustments to prevent file upload bottlenecks and database timeouts during initial installation and catalog operations.
The Dockerfile
Create a Dockerfile that layers our required PHP configuration adjustments on top of the official PrestaShop image:
# Base image: Official PrestaShop 8.1 with Apache & PHP 8.1
FROM prestashop/prestashop:8.1-apache
# Custom PHP configuration for high performance
RUN echo "memory_limit = 512M\n\
upload_max_filesize = 64M\n\
post_max_size = 64M\n\
max_execution_time = 300\n\
max_input_vars = 5000" > /usr/local/etc/php/conf.d/custom-prestashop.ini
# Expose standard web port
EXPOSE 80
Explaining the Technical Rationale
-
memory_limit = 512M: PrestaShop module compilations and database migrations exhaust PHP default limits (128M), causing fatal memory allocation errors during installation. -
max_input_vars = 5000: Back-office catalog menus and product variations post hundreds of form parameters in a single HTTP request. The PHP default (1000) causes silent data truncation. -
max_execution_time = 300: Allows long-running database population steps to complete without Apache terminating the gateway worker thread.
Building and Pushing to Amazon ECR
Create an Amazon ECR repository and publish the image with an explicit release tag:
# 1. Create the repository
aws ecr create-repository --repository-name prestashop --region us-east-1
# 2. Authenticate Docker with Amazon ECR
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin <ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com
# 3. Build the container image
docker build -t prestashop:8.1 .
# 4. Tag the container image for ECR
docker tag prestashop:8.1 <ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/prestashop:8.1
# 5. Push the image
docker push <ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/prestashop:8.1
Always tag container images with a semantic version (such as :8.1) rather than relying on :latest. As I will demonstrate in the troubleshooting section, relying on untagged references creates immediate deployment failures in Kubernetes.
Step 4: Provisioning the Amazon EKS Cluster and Worker Nodes
With the network and container repository established, we provision the Kubernetes control plane and compute nodes.
Required IAM Roles
EKS operates on two distinct permission layers:
-
Cluster Role (
prestashop-eks-cluster-role): Allows Kubernetes control plane components to manage AWS resources (such as load balancers and elastic network interfaces). Attach the AWS-managed policyAmazonEKSClusterPolicy. -
Node Instance Role (
prestashop-eks-node-role): Assigned to the EC2 worker nodes. Attach three mandatory AWS-managed policies:-
AmazonEKSWorkerNodePolicy: Allows worker nodes to register with the control plane. -
AmazonEC2ContainerRegistryReadOnly: Permits pulling images from Amazon ECR. -
AmazonEKS_CNI_Policy: Enables the AWS VPC CNI plugin to allocate and assign VPC IP addresses to pods.
-
Cluster Creation and Endpoint Access Architecture
Create the EKS cluster within your custom VPC.
A vital architectural setting is Cluster Endpoint Access:
-
Private Access: MUST be enabled. This creates cross-account Elastic Network Interfaces (ENIs) directly inside your private subnets, allowing worker node
kubeletdaemons to communicate with the Kubernetes API server internally without traversing the public internet. -
Public Access: Enabled with CIDR restrictions so you can execute administrative
kubectlcommands from your local machine.
Node Group Sizing and the Private Subnet Placement
Provision the Managed Node Group placed exclusively inside private-app-subnet-1 and private-app-subnet-2:
- Instance type:
t3.small(Free Tier eligible on modern accounts, supporting 110 pods per node and 2 GiB of memory). - Desired capacity: 2 nodes. Minimum: 1, Maximum: 3.
Authorizing EKS Pods to Access RDS
Update the inbound rules of the RDS security group (rds-db-sg):
- Protocol: TCP
- Port: 3306 (MySQL)
- Source: The Security Group ID of the EKS Worker Nodes.
This ensures that only workloads running on verified cluster nodes can establish socket connections with the database.
Step 5: Kubernetes Manifests and Zero-Trust Secrets Integration
Rather than creating Kubernetes secrets manually with base64-encoded strings, we integrate the cluster directly with AWS Secrets Manager using the Secrets Store CSI Driver and IAM Roles for Service Accounts (IRSA).
Step 5.1: Configure IAM Roles for Service Accounts (IRSA)
IRSA eliminates the need to store long-lived AWS credentials inside Kubernetes worker nodes.
# 1. Associate the IAM OIDC Provider with your cluster
eksctl utils associate-iam-oidc-provider \
--region us-east-1 \
--cluster your-cluster-name \
--approve
# 2. Create the IAM policy granting access to Secrets Manager
aws iam create-policy \
--policy-name prestashop-secret-access-policy \
--policy-document '{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret"
],
"Resource": "arn:aws:secretsmanager:us-east-1:441356679525:secret:prestashop/db/credentials-8pAAsz"
}
]
}'
# 3. Create the Kubernetes namespace
kubectl create namespace prestashop
# 4. Bind the Kubernetes Service Account to the IAM Policy
eksctl create iamserviceaccount \
--name prestashop-service-account \
--namespace prestashop \
--region us-east-1 \
--cluster your-eks-cluster-name \
--attach-policy-arn "arn:aws:iam::<ACCOUNT_ID>:policy/prestashop-secret-access-policy" \
--approve \
--override-existing-serviceaccounts
Step 5.2: Install the Secrets Store CSI Driver with STS Support
Install the driver using Helm, passing the STS token audience flag:
# Add the Helm repository
helm repo add secrets-store-csi-driver https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts
helm repo update
# Install the CSI driver with secret synchronization and STS tokens enabled
helm install csi-secrets-store secrets-store-csi-driver/secrets-store-csi-driver \
--namespace kube-system \
--set syncSecret.enabled=true \
--set "tokenRequests[0].audience=sts.amazonaws.com"
# Install the official AWS Provider plugin
kubectl apply -f https://raw.githubusercontent.com/aws/secrets-store-csi-driver-provider-aws/main/deployment/aws-provider-installer.yaml
Step 5.3: The SecretProviderClass Manifest (secret-provider-class.yaml)
This custom resource instructs the CSI driver which secret object to retrieve from AWS Secrets Manager and maps the JSON keys (username and password) into a synchronized Kubernetes Secret:
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: prestashop-secret-provider-class
namespace: prestashop
spec:
provider: aws
secretObjects:
- secretName: prestashop-db-secret
type: Opaque
data:
- objectName: DB_PASSWORD
key: password
- objectName: DB_USER
key: username
parameters:
objects: |
- objectName: "arn:aws:secretsmanager:us-east-1:<ACCOUNT_ID>:secret:prestashop/db/credentials-8pAAsz"
jmesPath:
- path: password
objectAlias: DB_PASSWORD
- path: username
objectAlias: DB_USER
Why This Design Matters
The CSI driver retrieves the secret from AWS over an encrypted channel using short-lived tokens. The secretObjects directive synchronizes these values into a native Kubernetes Secret (prestashop-db-secret), allowing pods to consume them as environment variables without exposing raw values in Git repositories.
Step 5.4: The Deployment Manifest (deployment.yaml)
Deploy PrestaShop into the prestashop namespace, referencing the Service Account and mounting the inline CSI volume:
apiVersion: apps/v1
kind: Deployment
metadata:
name: prestashop-deployment
namespace: prestashop
labels:
app: prestashop
spec:
replicas: 1
selector:
matchLabels:
app: prestashop
template:
metadata:
labels:
app: prestashop
spec:
serviceAccountName: prestashop-service-account
volumes:
- name: secrets-store-inline
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: prestashop-secret-provider-class
containers:
- name: prestashop
image: 441356679525.dkr.ecr.us-east-1.amazonaws.com/prestashop:8.1
imagePullPolicy: Always
ports:
- containerPort: 80
env:
- name: DB_SERVER
value: "prestashop-db.cepkyuma02s4.us-east-1.rds.amazonaws.com"
- name: DB_NAME
value: "prestashop"
- name: DB_USER
valueFrom:
secretKeyRef:
name: prestashop-db-secret
key: username
- name: DB_PASSWD
valueFrom:
secretKeyRef:
name: prestashop-db-secret
key: password
- name: PS_HANDLE_REVERSE_PROXY
value: "1"
volumeMounts:
- name: secrets-store-inline
mountPath: "/mnt/secrets-store"
readOnly: true
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
Explaining the Configuration
-
volumeMounts: In Kubernetes Secrets Store CSI architecture, secrets are only fetched from AWS when a pod mounts the CSI volume. Mounting the volume triggers the retrieval, after which the synchronized Kubernetes secret is generated. -
PS_HANDLE_REVERSE_PROXY=1: Informs PrestaShop that client SSL termination occurs upstream at the AWS load balancer. Without this setting, PrestaShop enters infinite redirection loops. - Explicit CPU and memory limits ensure that the PHP worker threads do not cause node-level memory exhaustion.
Step 5.5: The Service Manifest (service.yaml)
Expose the application through an AWS Network Load Balancer (NLB):
apiVersion: v1
kind: Service
metadata:
name: prestashop-service
namespace: prestashop
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
service.beta.kubernetes.io/aws-load-balancer-internal: "false"
service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
spec:
type: LoadBalancer
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
selector:
app: prestashop
Apply all three manifests:
kubectl apply -f secret-provider-class.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
Step 6: Post-Installation Hardening inside the Running Pod
Once the PrestaShop installation assistant completes database population via the browser, PrestaShop locks access to the administration dashboard until two mandatory security steps are performed:
- Deleting the
/installdirectory to prevent unauthorized re-installation. - Renaming the default
/admindirectory to an unguessable unique identifier.
In a traditional VM environment, you would SSH into the server to execute filesystem changes. In Kubernetes, we execute these commands directly inside the active container using kubectl exec:
# 1. Obtain the running pod name
POD_NAME=$(kubectl get pods -n prestashop -l app=prestashop -o jsonpath="{.items[0].metadata.name}")
# 2. Delete the install directory
kubectl exec -it -n prestashop $POD_NAME -- rm -rf /var/www/html/install
# 3. Rename the admin directory to the suggested unique path
kubectl exec -it -n prestashop $POD_NAME -- mv /var/www/html/admin /var/www/html/admin107nz3byc5pv2nr5xgj
Once executed, the administrative back-office becomes immediately accessible at:
http://<LOAD_BALANCER_DNS>/admin107nz3byc5pv2nr5xgj/
The Build Log: Real-World Hiccups and How I Resolved Them
Infrastructure projects rarely proceed without obstacles. Production engineering is defined by how you diagnose and resolve unexpected failures. Below are the actual roadblocks encountered during this deployment, along with the diagnostic processes and technical resolutions.
Hiccup 1: Homebrew Dependency Deadlocks on macOS
-
The Symptom: Running
brew install kubectlon macOS 13 triggered Homebrew to build eleven low-level GNU dependencies (ncurses,bash,gettext,libunistring,coreutils) from source. The build stalled indefinitely due to network timeouts when connecting toftp.gnu.org. - The Root Cause: Homebrew deprecates older macOS releases and ceases distributing pre-compiled binary packages ("bottles"), forcing source compilation on missing dependencies.
-
The Solution: Bypass package managers entirely for standalone Go and compiled binaries. I installed official pre-built binaries directly into
/usr/local/binusingcurl:
# Direct install for kubectl
curl -LO "https://dl.k8s.io/release/v1.31.0/bin/darwin/amd64/kubectl"
chmod +x ./kubectl && sudo mv ./kubectl /usr/local/bin/kubectl
# Direct install for eksctl
curl -sLO "https://github.com/eksctl-io/eksctl/releases/latest/download/eksctl_Darwin_amd64.tar.gz"
tar -xzf eksctl_Darwin_amd64.tar.gz -C /tmp && sudo mv /tmp/eksctl /usr/local/bin/eksctl
# Direct install for helm
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh && ./get_helm.sh && rm get_helm.sh
Hiccup 2: The AWS Free Tier Pod Density Bottleneck (Too many pods)(NodeCreationFailure)
-
The Symptom: Core Kubernetes components (
coredns,metrics-server, and the Secrets Store CSI driver) remained permanently stuck inPendingstate. Describing the pending pods returned:0/2 nodes are available: 2 Too many pods. -
The Root Cause: When initially attempting to stay within Free Tier limits using
t3.microinstances, AWS hardcodes a maximum limit of 4 pods per node. AWS calculates this limit based on the physical network cards (ENIs) of the instance:
$$\text{Max Pods} = (\text{Number of ENIs} \times (\text{IPs per ENI} - 1)) + 2$$
For t3.micro, $(2 \times (2 - 1)) + 2 = 4\text{ pods}$. Because AWS system daemons (aws-node, kube-proxy, eks-pod-identity-agent, eks-node-monitoring-agent) occupied all four slots on both nodes, the cluster was operating at 100% capacity before application pods were even scheduled.
-
The Solution: Rather than launching excessive micro nodes, I reviewed AWS instance policies and identified that modern AWS accounts include
t3.smallin their Free Tier allowance. Moving tot3.smallprovided:- 110 pods per node, accommodating all system agents, CoreDNS, CSI drivers, and PrestaShop.
- 2 GiB of RAM per node, providing necessary memory margins for Apache and PHP worker processes.
Hiccup 3: Secrets Store CSI Driver STS Token Trap
-
The Symptom: The PrestaShop pod remained stuck in
ContainerCreatingfor hours. Inspecting pod events (kubectl describe pod) revealed:Warning FailedMount: MountVolume.SetUp failed for volume "secrets-store-inline" : rpc error: code = Unknown desc = CSI token error: serviceAccount.tokens not provided - ensure tokenRequests is configured in CSIDriver spec -
The Root Cause: Modern Kubernetes releases require the Secrets Store CSI Driver to request short-lived projected service account tokens (
sts.amazonaws.com) from the Kubernetes API server before communicating with the AWS Provider. The CSI driver had been installed without the STS token audience configured, preventing the provider from assuming the IAM role via Web Identity Federation. - The Solution: Upgraded the Helm release to inject the STS audience configuration and recycled the stalled pod:
# Upgrade Helm release with STS token request configured
helm upgrade csi-secrets-store secrets-store-csi-driver/secrets-store-csi-driver \
--namespace kube-system \
--set syncSecret.enabled=true \
--set "tokenRequests[0].audience=sts.amazonaws.com"
# Delete the blocked pod to trigger an immediate fresh mount attempt
kubectl delete pod -n prestashop -l app=prestashop
Hiccup 4: Rolling Update Slot Deadlock and Image Tag Mismatch
-
The Symptom: Following the CSI fix, the new pod entered
ImagePullBackOff. When the deployment was updated, a new pod remained stuck inPendingwhile the old pod sat inImagePullBackOff. -
The Root Cause: Two issues compounded here:
- The container image specified in
deployment.yamllacked an explicit tag, causing Kubernetes to request:latestfrom Amazon ECR. Because the image was pushed as:8.1, ECR rejected the pull. - When the image was updated, Kubernetes initiated a rolling update by attempting to start the new replica before terminating the old replica. Because the node was near its pod allocation ceiling, the failed pod occupied the final slot, preventing the new pod from scheduling.
- The container image specified in
-
The Solution: Updated the deployment with the explicit
:8.1image tag and manually deleted the stuck pod so the new replica could claim the available node slot immediately:
# Update image tag
kubectl set image deployment/prestashop-deployment prestashop=441356679525.dkr.ecr.us-east-1.amazonaws.com/prestashop:8.1 -n prestashop
# Remove the failed pod occupying the node slot
kubectl delete pod -n prestashop prestashop-deployment-686796b775-s7k2z
Hiccup 5: The Reverse Proxy SSL Enforcement Loop
- The Symptom: On the day following initial deployment, the storefront suddenly stopped loading in web browsers, presenting a connection timeout error.
-
The Root Cause: Inspecting raw HTTP headers with
curl -ILrevealed that Apache was returning:Status: 301 Moved PermanentlyLocation: https://aad1534e7...elb.amazonaws.com/
PrestaShop had internal SSL enforcement enabled (PS_SSL_ENABLED=1) in its database. Because the AWS Network Load Balancer was currently configured to listen only on HTTP port 80, the browser followed the 301 redirect to HTTPS port 443, where no listener existed, resulting in a connection timeout.
- The Solution: Executed a PHP one-liner directly inside the pod to toggle SSL enforcement off in the database until an ACM SSL certificate and HTTPS listener were attached to the load balancer:
kubectl exec -it -n prestashop $(kubectl get pods -n prestashop -l app=prestashop -o jsonpath="{.items[0].metadata.name}") -- php -r '
$db = new PDO("mysql:host=" . getenv("DB_SERVER") . ";dbname=" . getenv("DB_NAME"), getenv("DB_USER"), getenv("DB_PASSWD"));
$db->exec("UPDATE ps_configuration SET value = 0 WHERE name IN (\"PS_SSL_ENABLED\", \"PS_SSL_ENABLED_EVERYWHERE\");");
echo "SSL Disabled successfully\n";
'
Once executed and tested in a private browser session to avoid cached redirects, the storefront loaded immediately over HTTP.
What I Would Improve in a v2
While this deployment satisfies zero-trust security and multi-AZ availability requirements, production engineering requires continuous iteration. For a version 2 of this architecture, I would implement:
-
Shared Persistent Storage via Amazon EFS: PrestaShop stores customer uploads, module configurations, and product imagery on the local container filesystem. Scaling this deployment to multiple pod replicas across Availability Zones requires decoupling state from the container using the Amazon EFS CSI Driver with
ReadWriteMany(RWX) volume mounts. -
Infrastructure as Code (IaC) with Terraform: Replace manual AWS CLI and
eksctlcommands with modular Terraform configurations, managing VPC topology, RDS instances, IAM roles, and EKS clusters declaratively with remote state locking. -
Automated GitOps Continuous Delivery: Implement ArgoCD to synchronize Kubernetes manifests directly from a Git repository, eliminating manual
kubectl applycommands and enforcing automated drift correction. - End-to-End TLS Termination with AWS Load Balancer Controller: Provision an AWS Application Load Balancer (ALB) using the AWS Load Balancer Controller, automatically requesting and renewing SSL certificates via AWS Certificate Manager (ACM) and managing DNS records with Route 53.
What Is Next
In my next project, I will transition from script-driven and CLI deployments to fully declarative Infrastructure as Code (IaC) using Terraform, provisioning multi-environment cloud platforms with automated testing and continuous integration pipelines.
You can view the complete source code, Docker configuration, and Kubernetes manifests for this project in the repository below:
Top comments (0)