
If you’ve ever inherited a PrestaShop server that someone else “quickly set up” over a weekend, you already know the pain: mismatched PHP extensions, a database user with root privileges it never needed, and a wp-config-style file sitting in a world-readable directory. This guide skips that path entirely. It walks through installing PrestaShop on Ubuntu 26.04 LTS the way you’d actually want it running in production — hardened, tuned, and predictable under load.
Ubuntu 26.04 LTS ships with a modern kernel, updated OpenSSL libraries, and repository defaults that finally align with where PHP and MySQL ecosystems have moved. That matters for PrestaShop specifically, because the platform’s latest major release — PrestaShop 9.1 — now recommends PHP 8.5 and leans on Symfony 6.4 LTS under the hood. Running an e-commerce store on outdated PHP isn’t just a performance tax anymore; it’s a security liability, since abandoned PHP branches stop receiving patches while your customer payment data sits exposed.
This isn’t a copy-paste tutorial dressed up with screenshots. It’s written the way a sysadmin actually approaches a fresh deployment: plan the stack, provision dependencies deliberately, install with intent, then immediately lock the thing down before it ever touches real traffic. Whether you’re standing up a client’s first online shop or migrating an existing PrestaShop instance to newer infrastructure, the sequence below reflects what actually breaks in the field — not just what the official documentation glosses over.
By the end, you’ll have a working PrestaShop installation on Ubuntu 26.04, a web server configured for real traffic patterns, a database tuned rather than left on defaults, and a checklist for the security work that too many “quick installs” skip entirely.
Why Ubuntu 26.04 LTS Makes Sense for PrestaShop
Ubuntu 26.04 LTS gives you five years of guaranteed security patching, which is non-negotiable for anything processing customer payment information. It also ships with repository packages close enough to PHP 8.3/8.4 that adding the Ondřej Surý PPA for PHP 8.5 is a clean, low-friction step rather than a fight against dependency conflicts.
There’s a practical reason to care about PHP version alignment here: PrestaShop 9.1 officially recommends PHP 8.5, supports 8.1 through 8.5, and explicitly drops PHP 7.x support. If you’re still running a PrestaShop 8.x store, you have more PHP flexibility (7.2.5–8.1), but anyone building fresh in 2026 should default to PrestaShop 9.1 on PHP 8.5 unless a specific module forces a downgrade.
Prerequisites Before You Touch the Terminal
Before running a single command, confirm the server meets these baseline requirements:
- Ubuntu 26.04 LTS with at least 2 vCPUs and 4GB RAM (2GB is technically enough for testing, not for production checkout flows)
- Minimum 20GB SSD storage — PrestaShop’s image cache and logs grow faster than most people expect
- A registered domain with DNS already pointing to the server’s IP
- Root or sudo access via SSH
- A non-root user with sudo privileges for daily administration
Skipping the domain/DNS step is a common mistake — Let’s Encrypt certificate issuance later in this guide requires a resolvable domain, and there’s no point installing PrestaShop before that DNS propagation window has passed.
Step 1: Update the System and Set the Hostname
Start clean. A freshly provisioned VPS often has stale package indexes or leftover cloud-init artifacts.
sudo apt update && sudo apt upgrade -y
sudo hostnamectl set-hostname shop.yourdomain.com
sudo reboot
Rebooting after a kernel update isn’t optional if you want accurate uptime monitoring later — running on a stale kernel while uname -r shows something six versions old is a small thing that trips up incident response during an actual outage.
Step 2: Install and Configure Nginx
Apache still works fine with PrestaShop, and older tutorials default to it, but Nginx handles PrestaShop’s static asset load (product images, theme CSS/JS) with noticeably lower memory overhead under concurrent traffic — which matters the moment a flash sale or ad campaign sends a traffic spike your way.
sudo apt install nginx -y
sudo systemctl enable --now nginx
sudo ufw allow 'Nginx Full'
Verify it’s serving by hitting the server IP in a browser — you should see the default Nginx welcome page.
Step 3: Install PHP 8.5 and Required Extensions
Ubuntu 26.04’s default repos may lag slightly behind the bleeding edge, so pulling from the Ondřej Surý PPA guarantees access to PHP 8.5 with timely security backports.
sudo apt install software-properties-common -y
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update
sudo apt install php8.5-fpm php8.5-cli php8.5-mysql php8.5-curl \
php8.5-gd php8.5-mbstring php8.5-xml php8.5-zip php8.5-intl \
php8.5-bcmath php8.5-soap php8.5-opcache unzip -y
Every one of those extensions matters for a specific PrestaShop feature: gd for image resizing during product uploads, intl for multi-currency/multi-language stores, soap for legacy payment gateway integrations, and bcmath for tax and price calculations where floating-point rounding would otherwise cause customer-facing discrepancies.
Now tune php.ini for e-commerce workloads — the defaults are written for generic websites, not catalog-heavy stores with bulk product imports:
sudo nano /etc/php/8.5/fpm/php.ini
Adjust these values:
memory_limit = 512M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 300
max_input_vars = 10000
date.timezone = Asia/Jakarta
The max_input_vars bump is easy to overlook and one of the most common causes of silent form-submission failures on PrestaShop’s admin panel, particularly when editing products with many combinations (size/color variants). If a store manager complains that saving a product with 15 variants “just doesn’t work,” this is usually why.
Restart PHP-FPM to apply changes:
sudo systemctl restart php8.5-fpm
Step 4: Install and Secure MySQL
PrestaShop 9.1 supports MySQL 5.7+ and MariaDB, but for new deployments, MySQL 8.0+ gives better query optimizer behavior for the JOIN-heavy queries PrestaShop’s catalog and order modules generate.
sudo apt install mysql-server -y
sudo mysql_secure_installation
Answer “yes” to removing anonymous users, disallowing remote root login, and removing the test database. Then create a dedicated, least-privilege database and user:
sudo mysql -u root -p
CREATE DATABASE prestashop_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'ps_user'@'localhost' IDENTIFIED BY 'Use_A_Strong_Random_Password_Here!';
GRANT ALL PRIVILEGES ON prestashop_db.* TO 'ps_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Note the utf8mb4 charset — not the older utf8 alias. This isn’t cosmetic; without full utf8mb4 support, product names containing emoji, certain accented characters common in Indonesian or European localized stores, or multi-byte symbols get silently truncated or corrupted during import.
Never grant a web-facing database user SUPER or FILE privileges. ALL PRIVILEGES on the specific database is already more permissive than most production setups need, but scoping it to the store’s own schema at least limits blast radius if the application layer is ever compromised.
Step 5: Download and Extract PrestaShop 9.1
cd /tmp
wget https://github.com/PrestaShop/PrestaShop/releases/download/9.1.0/prestashop_9.1.0.zip
unzip prestashop_9.1.0.zip -d prestashop_installer
sudo mkdir -p /var/www/shop
sudo unzip prestashop_installer/prestashop.zip -d /var/www/shop
sudo chown -R www-data:www-data /var/www/shop
sudo find /var/www/shop -type d -exec chmod 755 {} \;
sudo find /var/www/shop -type f -exec chmod 644 {} \;
Resist the temptation to chmod -R 777 anything, even temporarily “to get it working.” That habit from a decade-old forum thread has caused more compromised PrestaShop stores than any actual PHP vulnerability — a writable directory with execute permissions is an open invitation for uploaded shell scripts.
Step 6: Configure the Nginx Server Block
PrestaShop needs specific rewrite rules for its front controller and admin routing. Create a dedicated server block rather than editing the default one.
sudo nano /etc/nginx/sites-available/shop.yourdomain.com
server {
listen 80;
server_name shop.yourdomain.com www.shop.yourdomain.com;
root /var/www/shop;
index index.php;
client_max_body_size 64M;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.5-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ /\.(?!well-known).* {
deny all;
}
location ~* \.(jpg|jpeg|png|gif|css|js|woff2?)$ {
expires 30d;
access_log off;
}
}
Enable it and reload:
sudo ln -s /etc/nginx/sites-available/shop.yourdomain.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
That location ~ /\. block quietly denies access to hidden files like .env or .git — a detail generic tutorials frequently skip, and one that’s saved more than one store from leaking database credentials through a misconfigured version control leftover.
Step 7: Run the Web-Based Installer
Point a browser to http://shop.yourdomain.com. The installer walks through language selection, license agreement, and a system compatibility check that flags any missing PHP extensions before you proceed.
During the database step, enter the credentials created in Step 4 — prestashop_db, ps_user, and the password. Use “localhost” as the database server unless MySQL runs on a separate host. The installer then builds tables and prompts for store name, activity, and admin account details.
Set a genuinely strong admin password here — not “admin123” — because the next step depends on it.
Once installation completes, PrestaShop displays a randomized admin folder name (something like admin847xk2). Write it down immediately; losing track of it means digging through the database’s configuration table to recover it later.
Step 8: Post-Installation Cleanup
This step gets skipped more often than it should, and it’s the single most common reason PrestaShop stores get compromised within weeks of launch.
sudo rm -rf /var/www/shop/install
sudo chmod -R 755 /var/www/shop/admin847xk2
Leaving the /install directory intact after setup is a well-known attack vector — automated scanners actively probe for it, since a reachable installer can sometimes be tricked into re-running against an existing database.
Securing PrestaShop for Production
Enable HTTPS with Let’s Encrypt
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d shop.yourdomain.com -d www.shop.yourdomain.com
Certbot’s Nginx plugin automatically rewrites the server block for HTTPS and sets up auto-renewal via a systemd timer — verify it with sudo systemctl list-timers | grep certbot.
Configure the Firewall
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
Restrict SSH to key-based authentication only and disable password login in /etc/ssh/sshd_config — a basic step, but one that stops the overwhelming majority of automated credential-stuffing attempts against the server itself.
Harden File Permissions
PrestaShop’s config, cache, log, and upload directories need write access for www-data, but nothing else should. Audit permissions periodically:
find /var/www/shop -type f -perm /o+w -exec ls -l {} \;
Any world-writable file that shows up here deserves investigation — legitimate PrestaShop operation never requires it.
Set Security Headers in Nginx
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
Add these inside the server block and reload Nginx. They’re small additions with outsized impact against clickjacking and MIME-sniffing attacks.
Performance Tuning for Real Traffic
PHP-FPM Process Management
Default FPM pool settings are tuned for shared hosting, not a dedicated e-commerce server. Edit /etc/php/8.5/fpm/pool.d/www.conf:
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
Calculate pm.max_children based on available RAM divided by average PHP process memory usage — running ps aux | grep php-fpm under load gives a realistic per-process footprint to base this on, rather than guessing.
Enable OPcache
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
Setting validate_timestamps=0 in production means PHP won’t recheck file modification times on every request — a meaningful speed gain, but it also means you must manually clear OPcache (sudo systemctl restart php8.5-fpm) after every deployment or module update, or changes won’t take effect.
MySQL Tuning
Edit /etc/mysql/mysql.conf.d/mysqld.cnf:
innodb_buffer_pool_size = 1G
innodb_log_file_size = 256M
query_cache_type = 0
max_connections = 150
innodb_buffer_pool_size should generally sit around 60-70% of available RAM on a dedicated database server — set it too low and every catalog query hits disk unnecessarily during peak traffic.
Enable Nginx Gzip and Caching
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml;
gzip_comp_level 5;
Pair this with a CDN in front of product images for stores with large catalogs — offloading static asset delivery reduces origin server load dramatically during traffic spikes from promotions or paid campaigns.
Troubleshooting Common Installation Errors
“Database connection failed” during installer
Usually a credentials mismatch or MySQL not listening on the expected socket. Verify with sudo mysql -u ps_user -p prestashop_db directly — if that fails, the installer never had a chance.
500 Internal Server Error after install
Check /var/log/nginx/error.log and /var/www/shop/var/logs/ first. Nine times out of ten, it’s a permissions issue on the var/cache directory or a missing PHP extension the compatibility check somehow didn’t catch.
Blank white screen with no error
PHP errors are likely suppressed. Temporarily set display_errors = On in php.ini, reproduce the issue, then revert immediately — never leave error display on in production, since it leaks file paths and configuration details to anyone who triggers an error.
Admin panel returns 403 Forbidden
Check the Nginx location ~ /\. block isn’t accidentally matching the renamed admin folder if it starts with a dot-like character, and confirm fastcgi_pass points to the correct PHP-FPM socket version.
Images not displaying after migration
PrestaShop stores image paths in the database tied to specific folder structures under /img. A common cause is incomplete file transfer — verify with find /var/www/shop/img -type f | wc -l against the expected product count.