How To Install PrestaShop on Ubuntu 26.04 LTS

Install PrestaShop on Ubuntu 26.04

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.

r00t is a Linux Systems Administrator and open-source advocate with over ten years of hands-on experience in server infrastructure, system hardening, and performance tuning. Having worked across distributions such as Debian, Arch, RHEL, and Ubuntu, he brings real-world depth to every article published on this blog. r00t writes to bridge the gap between complex sysadmin concepts and practical, everyday application — whether you are configuring your first server or optimizing a production environment. Based in New York, US, he is a firm believer that knowledge, like open-source software, is best when shared freely.

Related Posts