How to Install WooCommerce on Ubuntu 24.04 VPS — Self-Hosted WordPress Store
WooCommerce powers nearly a third of all online stores because it turns a standard WordPress install into a full-featured ecommerce platform without locking you into a hosted marketplace. Running it on your own Ubuntu 24.04 VPS gives you full database ownership, unlimited products, zero transaction fees, and the freedom to integrate any payment gateway, tax engine, or shipping provider you need. This guide walks you through installing and hardening WooCommerce on top of a LEMP stack, from PHP extensions to Redis object caching and system-level cron.
Skip the setup? Deploy a pre-configured WooCommerce store in one click with our Ecommerce VPS image. Launch a WooCommerce-ready VPS now and start selling in under 60 seconds.
Table of Contents
What is WooCommerce?
WooCommerce is an open-source ecommerce plugin for WordPress, originally developed by WooThemes and now maintained by Automattic, the company behind WordPress.com. It turns any WordPress site into a fully functional online store capable of selling physical products, digital downloads, subscriptions, bookings, and services. Because it is just a plugin, you keep everything WordPress already gives you — themes, SEO plugins, the Gutenberg editor, multilingual support — and add a complete commerce layer on top.
Out of the box, WooCommerce handles product catalogs with variations (size, color, material), inventory tracking, tax calculation, multi-currency storefronts, coupon codes, customer accounts, order management, refunds, and transactional emails. The plugin ecosystem extends it in every direction: subscriptions via WooCommerce Subscriptions, memberships via WooCommerce Memberships, bookings via WooCommerce Bookings, point-of-sale integration, marketplace functionality, dropshipping connectors, and hundreds of shipping and payment gateway integrations. For developers, a well-documented REST API and hook system make it straightforward to build custom checkout flows, connect to ERPs, or integrate with headless frontends built in Next.js or Nuxt.
Typical WooCommerce deployments range from single-product creator stores selling PDFs or courses, to mid-market retailers moving thousands of SKUs per month, to multi-vendor marketplaces using Dokan or WC Vendors. Self-hosting it on a VPS like Ubuntu 24.04 gives you full control over the database, server resources, caching strategy, and third-party services — something hosted SaaS platforms simply cannot match.
Why Self-Host WooCommerce Instead of Using Shopify?
Choosing between WooCommerce on your own VPS and a hosted platform like Shopify or BigCommerce comes down to cost, control, and ownership. The tradeoffs favor self-hosting for most serious stores:
- Zero transaction fees -- Shopify charges 0.5% to 2% per transaction on top of payment processor fees unless you use Shopify Payments. WooCommerce charges nothing. On $100,000 in annual sales, that saves $500 to $2,000 per year in platform fees alone.
- Full database ownership -- Your products, orders, customers, and analytics live in your own MariaDB database on your own VPS. You can export, back up, migrate, or query it directly. No vendor can lock you out, raise prices, or change terms.
- Unlimited products and variations -- Shopify's entry tier limits product variants. WooCommerce has no artificial limits; only your database schema and server resources matter. Stores with 50,000+ SKUs run comfortably on a well-tuned VPS.
- Flat, predictable cost -- A CloudCore Professional VPS handles small-to-mid traffic stores for EUR 19.99/month. Shopify's equivalent tier starts at $79/month and climbs sharply with traffic and features.
- Any payment gateway you want -- Shopify penalizes you for using non-Shopify Payments gateways. WooCommerce integrates with hundreds of gateways — Stripe, PayPal, Mollie, Klarna, Square, Razorpay, regional providers — with no platform surcharge.
- Full code access -- Every theme file, every plugin hook, every database column is yours to modify. You can build custom checkout experiences, integrate with internal ERPs, or fork any plugin to fit your workflow.
- SEO and content flexibility -- WordPress is still the best blogging and content platform on the web. WooCommerce inherits that, letting you build a content-driven commerce site — buyer guides, comparison posts, tutorials — that ranks on Google and drives organic traffic.
- GDPR and data residency -- Your customer data sits on your chosen server in your chosen jurisdiction. Easy to comply with EU, UK, or regional data residency rules.
Cost Comparison: Self-Hosted WooCommerce vs. Shopify
| Scenario | Shopify Basic | Shopify Advanced | Self-Hosted WooCommerce (VPS) |
|---|---|---|---|
| Monthly platform cost | $39/mo | $399/mo | EUR 19.99/mo |
| Transaction fee (non-native gateway) | 2.0% | 0.5% | 0% |
| Product limit | Unlimited | Unlimited | Unlimited |
| Staff accounts | 2 | 15 | Unlimited |
| Custom checkout | No | Limited | Full code access |
| Database access | No | No | Yes |
| Typical cost at $100K/year revenue | ~$2,460/yr | ~$5,290/yr | ~EUR 240/yr |
Prerequisites
Before starting, make sure you have:
- A VPS running Ubuntu 24.04 LTS with root or sudo access
- A LEMP stack already installed (Nginx, MariaDB, PHP-FPM) — follow our LEMP stack install guide if you have not set this up yet
- WordPress installed and reachable via HTTPS — follow How to Install WordPress on Ubuntu 24.04 first
- A registered domain pointed at your server with a valid SSL certificate (Let's Encrypt)
- MariaDB 10.6 or newer — see our MariaDB install guide if needed
- PHP 8.3 (recommended by WooCommerce for performance and long-term support)
- At least 4 GB of RAM and 40 GB of disk space for small stores; 8 GB+ for mid-sized catalogs
Recommended Plan: CloudCore Professional>
For a production WooCommerce store with Redis, FastCGI cache, and room for a product catalog of several thousand SKUs, we recommend the CloudCore Professional plan:>
- 6 vCPU cores
- 12 GB RAM
- 100 GB NVMe SSD
- Unmetered bandwidth
- EUR 19.99/month>
This handles 20,000-50,000 monthly visitors comfortably when combined with the caching and tuning steps in this guide.
Connect to your server via SSH:
ssh root@your-server-ipStep 1: Prepare the LEMP Stack and PHP Extensions
WooCommerce has specific PHP extension requirements beyond what base WordPress needs. Install all of them before activating the plugin to avoid runtime errors during checkout.
Start by updating the package index:
sudo apt update && sudo apt upgrade -yInstall the PHP 8.3 extensions required or recommended by WooCommerce:
sudo apt install -y \
php8.3-cli php8.3-fpm php8.3-common \
php8.3-mysql php8.3-curl php8.3-gd \
php8.3-mbstring php8.3-xml php8.3-zip \
php8.3-bcmath php8.3-intl php8.3-soap \
php8.3-imagick php8.3-redis php8.3-opcacheExpected output (abbreviated):
The following additional packages will be installed:
php8.3-cli php8.3-common php8.3-opcache ...
Setting up php8.3-gd (8.3.x-1+ubuntu24.04.1+deb.sury.org+1) ...
Setting up php8.3-imagick (3.7.0-4ubuntu2) ...
Setting up php8.3-redis (6.0.2-1+ubuntu24.04.1+deb.sury.org+1) ...Why each extension matters for WooCommerce:
php8.3-curl-- required for payment gateway API calls (Stripe, PayPal, etc.)php8.3-gd+php8.3-imagick-- product image resizing and thumbnailsphp8.3-mbstring-- handling multi-byte strings in product descriptions and international contentphp8.3-bcmath-- precise decimal arithmetic for order totals and tax calculation (never use floats for money)php8.3-intl-- currency formatting and localization across international storefrontsphp8.3-soap-- required by some carrier shipping integrations (UPS, FedEx)php8.3-zip-- plugin/theme upload and digital product downloadsphp8.3-redis-- connects WordPress to the Redis object cache (Step 9)php8.3-opcache-- bytecode caching; mandatory for any production WordPress site
sudo systemctl restart php8.3-fpmVerify all extensions are loaded:
php -m | grep -E 'curl|gd|mbstring|bcmath|intl|soap|zip|redis|imagick|opcache'Expected output:
bcmath
curl
gd
imagick
intl
mbstring
opcache
redis
soap
zipStep 2: Tune PHP and MariaDB for WooCommerce
WooCommerce checkout and admin pages are heavier than typical WordPress pages. The defaults ship with Ubuntu are too low for a real store.
Edit the PHP-FPM config:
sudo nano /etc/php/8.3/fpm/php.iniUpdate the following values:
memory_limit = 512M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 300
max_input_time = 300
max_input_vars = 5000memory_limit = 512M-- WooCommerce admin, especially reports and bulk product edits, commonly pushes past the 256M default.max_input_vars = 5000-- products with many variations (sizes x colors x materials) submit thousands of form fields. 1000 default is a frequent cause of "some variations failed to save" errors.max_execution_time = 300-- CSV product imports and bulk order operations need headroom.
sudo nano /etc/php/8.3/fpm/conf.d/10-opcache.iniopcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.revalidate_freq=60
opcache.validate_timestamps=1Restart PHP-FPM:
sudo systemctl restart php8.3-fpmNext, tune MariaDB. Edit /etc/mysql/mariadb.conf.d/50-server.cnf and add (or adjust) under [mysqld]:
innodb_buffer_pool_size = 2G
innodb_log_file_size = 256M
innodb_flush_log_at_trx_commit = 2
max_connections = 150
query_cache_type = 0
query_cache_size = 0For a 12 GB RAM VPS, innodb_buffer_pool_size = 2G is a safe starting point. WooCommerce stores order and product metadata in wp_postmeta, which can grow large quickly — a well-sized InnoDB buffer pool is the single biggest lever for store performance.
Restart MariaDB:
sudo systemctl restart mariadbStep 3: Install WooCommerce via WP-CLI
WP-CLI is the fastest way to install and configure WordPress plugins on the command line. If you followed our WordPress install guide, WP-CLI should already be available. If not:
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
wp --infoNavigate to your WordPress install directory:
cd /var/www/yourstore.comInstall and activate WooCommerce:
sudo -u www-data wp plugin install woocommerce --activateExpected output:
Installing WooCommerce (9.4.2)
Downloading installation package from https://downloads.wordpress.org/plugin/woocommerce.9.4.2.zip...
Unpacking the package...
Installing the plugin...
Plugin installed successfully.
Activating 'woocommerce'...
Plugin 'woocommerce' activated.
Success: Installed 1 of 1 plugins.Verify WooCommerce is active and check the version:
sudo -u www-data wp plugin list --status=activeExpected output:
+---------------+----------+--------+---------+
| name | status | update | version |
+---------------+----------+--------+---------+
| woocommerce | active | none | 9.4.2 |
+---------------+----------+--------+---------+Install a WooCommerce-compatible theme. Storefront is the official Automattic theme, but most users today choose Astra, Kadence, or a block theme like Twenty Twenty-Four with WooCommerce blocks:
sudo -u www-data wp theme install storefront --activateStep 4: Run the WooCommerce Setup Wizard
WooCommerce ships an onboarding wizard that configures your store's basic settings. You can complete it through the admin UI at https://yourstore.com/wp-admin/admin.php?page=wc-admin&path=/setup-wizard, or script it entirely via WP-CLI for automated deployments.
The wizard asks for:
- Store location -- country, state, and postcode. This sets default currency, default tax regions, and default shipping zones.
- Industry -- used for analytics and to surface relevant plugin recommendations.
- Product types -- physical, digital, subscriptions, bookings. Affects which plugins are suggested.
- Business details -- number of products, existing platform (for migration hints).
- Theme choice -- optional; skip if you already installed one.
sudo -u www-data wp option update woocommerce_store_address "123 Main St"
sudo -u www-data wp option update woocommerce_store_city "Berlin"
sudo -u www-data wp option update woocommerce_default_country "DE:BE"
sudo -u www-data wp option update woocommerce_store_postcode "10115"
sudo -u www-data wp option update woocommerce_currency "EUR"
sudo -u www-data wp option update woocommerce_product_type "physical"
sudo -u www-data wp option update woocommerce_allow_tracking "no"Create the essential WooCommerce pages (Shop, Cart, Checkout, My Account):
sudo -u www-data wp wc tool run install_pages --user=adminExpected output:
Success: Executed tool: Create default WooCommerce pagesVerify the pages exist:
sudo -u www-data wp post list --post_type=page --fields=ID,post_title,post_statusYou should see Shop, Cart, Checkout, My account, and Refund and Returns Policy in the list.
Step 5: Add Products, Variations, and Categories
You can add products via the admin UI (WooCommerce > Products > Add New) or through WP-CLI, which is dramatically faster for bulk loads. Start by creating product categories:
sudo -u www-data wp wc product_cat create --name="T-Shirts" --user=admin
sudo -u www-data wp wc product_cat create --name="Hoodies" --user=admin
sudo -u www-data wp wc product_cat create --name="Accessories" --user=adminCreate a simple product:
sudo -u www-data wp wc product create \
--name="Classic Logo T-Shirt" \
--type=simple \
--regular_price=24.99 \
--description="100% organic cotton t-shirt with our classic logo." \
--short_description="Organic cotton, unisex fit." \
--sku="TSHIRT-001" \
--manage_stock=true \
--stock_quantity=100 \
--user=adminVariable Products (Size, Color, Material)
Variable products need an attribute, then individual variations per combination. The UI is simpler for this; the flow is:
WooCommerce > Attributes -- create Size (Small, Medium, Large) and Color (Black, White, Navy).Products > Add New -- set product type to Variable product.Bulk CSV Import
For catalogs of hundreds or thousands of products, use the built-in CSV importer at WooCommerce > Products > Import. WooCommerce accepts its standard product CSV schema, including columns for variations, images (URLs or paths), categories, tags, stock, and custom attributes.
Increase the import batch size if you hit timeouts. Add to wp-config.php:
define( 'WC_PRODUCT_IMPORTER_BATCH_SIZE', 30 );Step 6: Configure Shipping Zones and Methods
Shipping in WooCommerce is organized into zones. Each zone is a set of regions (countries, states, postcodes), and each zone has one or more methods (flat rate, free shipping, local pickup, or carrier-calculated rates via plugins).
Navigate to WooCommerce > Settings > Shipping. Create zones that match how you actually ship:
- Domestic (Germany) -- flat rate EUR 4.99, free shipping over EUR 50
- European Union -- flat rate EUR 9.99
- Rest of World -- flat rate EUR 19.99 or disabled
sudo -u www-data wp wc shipping_zone create --name="Germany" --user=admin
sudo -u www-data wp wc shipping_zone_method create 1 \
--method_id=flat_rate \
--settings='{"title":"Standard Shipping","cost":"4.99"}' \
--user=adminFor real-time carrier rates (UPS, DHL, USPS, FedEx), install a carrier plugin — for example, the free WooCommerce Shipping plugin from Automattic or a paid integration like Table Rate Shipping for complex per-weight/per-zone pricing.
Shipping Classes
Use shipping classes to charge different rates per product type: heavy items, fragile items, oversized items. Create them under WooCommerce > Settings > Shipping > Shipping classes, assign products to classes on the product edit page, and then set per-class costs inside each flat rate method.
Step 7: Connect Payment Gateways (Stripe, PayPal, Bank Transfer)
WooCommerce ships with three payment methods enabled by default: Direct bank transfer, Check payments, and Cash on delivery. To accept card payments, install a gateway plugin.
Stripe (Cards, Apple Pay, Google Pay, SEPA)
sudo -u www-data wp plugin install woocommerce-gateway-stripe --activateNavigate to WooCommerce > Settings > Payments > Stripe and enter your Stripe publishable key and secret key from the Stripe dashboard. Enable Payment Request buttons for one-click Apple Pay / Google Pay checkout.
Configure the Stripe webhook endpoint so WooCommerce receives payment confirmations:
Developers > Webhooks > Add endpoint.https://yourstore.com/?wc-api=wc_stripe.charge.succeeded, charge.failed, charge.refunded, review.opened, review.closed, payment_intent.succeeded, payment_intent.payment_failed.WooCommerce > Settings > Payments > Stripe > Webhook secret.PayPal
sudo -u www-data wp plugin install woocommerce-paypal-payments --activateConnect via WooCommerce > Settings > Payments > PayPal Payments > Connect to PayPal. The plugin handles the OAuth flow and pulls your merchant credentials automatically.
Direct Bank Transfer (BACS)
Direct bank transfer is useful for B2B orders, wholesale customers, or markets where bank-to-bank transfers are common (Germany, Netherlands). Enable it under WooCommerce > Settings > Payments > Direct bank transfer and add your IBAN, BIC/SWIFT, and bank name. Customers see the bank details on the order confirmation page and the confirmation email.
Test Mode
Every gateway has a test/sandbox mode. Always place at least one successful test order and one refund before taking a store live. Stripe provides test card numbers like 4242 4242 4242 4242 for success and 4000 0000 0000 0002 for a decline.
Step 8: Set Up Tax Rules
Taxes in WooCommerce are configured under WooCommerce > Settings > Tax. Enable the tax system by going to WooCommerce > Settings > General and checking Enable taxes.
Key decisions:
- Prices entered with tax -- typical for EU/UK (customers see tax-inclusive prices). "No" for US (sales tax added at checkout).
- Calculate tax based on -- customer billing address, shipping address, or shop base address.
- Display prices in the shop -- including tax or excluding tax.
- Display prices during cart and checkout -- including or excluding tax.
Manual Tax Rates
For a simple EU store selling to one country, add rates manually at WooCommerce > Settings > Tax > Standard rates:
| Country | State | Postcode | City | Rate % | Tax Name | Shipping |
|---|---|---|---|---|---|---|
| DE | 19.0000 | VAT | ✓ | |||
| FR | 20.0000 | VAT | ✓ | |||
| ES | * | 21.0000 | VAT | ✓ |
Automated Tax Calculation
For cross-border commerce (EU OSS, US sales tax across 50 states, Canadian GST/HST), manual rates quickly become unmanageable. Use one of:
- WooCommerce Tax (free from Automattic) -- automatic rates for US, CA, UK, EU via Jetpack connection.
- Avalara AvaTax -- enterprise-grade; real-time rate lookup and tax return filing.
- TaxJar -- US-focused with strong nexus tracking and auto-filing.
Digital Goods (EU VAT MOSS)
If you sell digital downloads to EU consumers, you are required to charge VAT based on the customer's country, not yours. Enable Shop base address for physical goods but Customer billing address for digital, or use the EU VAT Assistant plugin which collects VAT numbers for B2B zero-rating and validates them against the VIES database automatically.
Step 9: Enable Redis Object Cache
WooCommerce hits the database hundreds of times per page load — product queries, cart state, session data, user meta. A Redis object cache reduces that to a handful of database queries and is the single largest performance win for a self-hosted store.
Install and start Redis (if not already running) by following our Redis install guide, then:
cd /var/www/yourstore.com
sudo -u www-data wp plugin install redis-cache --activateAdd the Redis connection details to wp-config.php:
sudo nano /var/www/yourstore.com/wp-config.phpAdd before the / That's all, stop editing! / line:
define( 'WP_REDIS_HOST', '127.0.0.1' );
define( 'WP_REDIS_PORT', 6379 );
define( 'WP_REDIS_DATABASE', 0 );
define( 'WP_REDIS_PREFIX', 'yourstore:' );
define( 'WP_REDIS_MAXTTL', 86400 );
define( 'WP_CACHE_KEY_SALT', 'yourstore.com' );Enable the drop-in:
sudo -u www-data wp redis enableExpected output:
Success: Object cache enabled.Verify it is working:
sudo -u www-data wp redis statusExpected output:
Status: Connected
Client: PhpRedis (v6.0.2)
Drop-in: ValidAfter 5-10 minutes of traffic, check the hit ratio:
redis-cli info stats | grep keyspaceA healthy WooCommerce site shows a 90%+ cache hit rate within an hour.
Step 10: Configure Nginx FastCGI Caching
While Redis caches database queries, FastCGI cache stores fully rendered HTML at the Nginx layer — skipping PHP entirely for anonymous visitors. This easily doubles your effective capacity.
Critical WooCommerce rule: never cache /cart, /checkout, /my-account, or any page with a logged-in user. Serving a cached cart to the wrong visitor is the classic WooCommerce caching disaster.
Edit your Nginx server block:
sudo nano /etc/nginx/sites-available/yourstore.comAdd at the top of the file (outside the server block):
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=WOOCACHE:100m max_size=1g inactive=60m use_temp_path=off;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
Inside the server { ... } block for your domain, add:
# Bypass cache for logged-in users and cart/checkout pages set $skip_cache 0;if ($request_method = POST) { set $skip_cache 1; } if ($query_string != "") { set $skip_cache 1; } if ($request_uri ~ "/wp-admin/|/wp-json/|/xmlrpc.php|wp-.\.php|^/feed/|sitemap(_index)?\.xml") { set $skip_cache 1; } if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in|woocommerce_items_in_cart|woocommerce_cart_hash") { set $skip_cache 1; } if ($request_uri ~* "/(cart|checkout|my-account)") { set $skip_cache 1; }
location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_cache_bypass $skip_cache; fastcgi_no_cache $skip_cache; fastcgi_cache WOOCACHE; fastcgi_cache_valid 200 301 302 60m; fastcgi_cache_use_stale error timeout invalid_header updating http_500 http_503; add_header X-FastCGI-Cache $upstream_cache_status; }
Create the cache directory and test the config:
sudo mkdir -p /var/cache/nginx/fastcgi
sudo chown www-data:www-data /var/cache/nginx/fastcgi
sudo nginx -t
sudo systemctl reload nginxVerify the cache is active:
curl -I https://yourstore.com/ | grep -i x-fastcgi-cacheFirst request shows MISS, subsequent requests show HIT:
X-FastCGI-Cache: HITInstall the Nginx Cache plugin so editing a product or page automatically purges the cache:
sudo -u www-data wp plugin install nginx-cache --activateIn Settings > Nginx Cache, set the cache path to /var/cache/nginx/fastcgi and enable auto-purge.
Step 11: Replace WP-Cron with a System Cron
WordPress triggers scheduled tasks (emails, Action Scheduler jobs, plugin cleanups) via wp-cron.php, which fires on every pageview. On a busy WooCommerce site, this adds latency to customer requests and causes missed jobs when traffic is low.
Replace it with a system cron. First, disable the built-in one by adding to wp-config.php:
define( 'DISABLE_WP_CRON', true );Then create a system cron job:
sudo crontab -u www-data -eAdd:
/5 * cd /var/www/yourstore.com && /usr/bin/php wp-cron.php > /dev/null 2>&1This runs every 5 minutes, independently of traffic. Verify it is scheduled:
sudo crontab -u www-data -lFor Action Scheduler (WooCommerce's queue for subscription renewals, email retries, order syncs), also add a dedicated runner so it processes batches more aggressively:
/1 * cd /var/www/yourstore.com && /usr/local/bin/wp action-scheduler run --batches=3 --batch-size=25 > /dev/null 2>&1Check queued actions:
sudo -u www-data wp action-scheduler statusExpected output:
Action Status Count
pending 12
in-progress 0
complete 1,482
failed 0Step 12: Harden WooCommerce and WordPress
Stores are high-value targets. The basic WordPress hardening steps apply, plus WooCommerce-specific ones.
Limit login attempts and protect wp-login.php
Install Limit Login Attempts Reloaded:
sudo -u www-data wp plugin install limit-login-attempts-reloaded --activateOr use Fail2ban with a WordPress jail for server-level protection.
Disable XML-RPC
XML-RPC is rarely used by WooCommerce but is a common brute-force target. Block it in Nginx:
location = /xmlrpc.php {
deny all;
access_log off;
log_not_found off;
return 444;
}Force HTTPS on checkout and admin
Add to wp-config.php:
define( 'FORCE_SSL_ADMIN', true );Nginx should already redirect all HTTP traffic to HTTPS at the server level.
Restrict file permissions
sudo find /var/www/yourstore.com -type d -exec chmod 755 {} \;
sudo find /var/www/yourstore.com -type f -exec chmod 644 {} \;
sudo chmod 600 /var/www/yourstore.com/wp-config.php
sudo chown -R www-data:www-data /var/www/yourstore.comWooCommerce-specific hardening
- Disable the REST API for unauthenticated users if you do not use a headless frontend. The Disable WP REST API plugin handles this cleanly.
- Restrict
/wp-json/wc/endpoints to authenticated API keys only. WooCommerce REST API keys are generated atWooCommerce > Settings > Advanced > REST API. - Enable two-factor authentication for all admin accounts using Two Factor or WP 2FA.
- Turn off file editing in the admin:
define( 'DISALLOW_FILE_EDIT', true );
define( 'DISALLOW_FILE_MODS', true );- Enable WooCommerce fraud tools — Stripe Radar, PayPal Seller Protection, or a plugin like FraudLabs Pro for velocity checks and blocklists.
- Install a WAF like CrowdSec or Cloudflare's free WAF rules for OWASP protection at the edge.
Daily security checks
sudo -u www-data wp core verify-checksums
sudo -u www-data wp plugin verify-checksums --allThese compare installed files against the official WordPress.org checksums. Any mismatch is a red flag.
Backup Strategy
A WooCommerce store loses money every hour it is offline, so backups need to be frequent, automated, and tested.
What to back up
- MariaDB database -- all product, order, and customer data
/var/www/yourstore.com/wp-content/uploads/-- product images, invoices, digital downloads/var/www/yourstore.com/wp-content/plugins/andthemes/-- if you use custom or modified codewp-config.php-- site-specific secrets and DB credentials
Daily database dump
Create /usr/local/bin/backup-woocommerce.sh:
#!/bin/bash
DATE=$(date +%Y%m%d-%H%M)
BACKUP_DIR=/var/backups/woocommerce
mkdir -p $BACKUP_DIRDatabase
mysqldump --single-transaction --quick --routines --triggers \
wordpress_db | gzip > $BACKUP_DIR/db-$DATE.sql.gzUploads
tar czf $BACKUP_DIR/uploads-$DATE.tar.gz \
-C /var/www/yourstore.com/wp-content uploadsKeep 14 daily + 8 weekly backups, then prune
find $BACKUP_DIR -name "db-*.sql.gz" -mtime +14 -delete
find $BACKUP_DIR -name "uploads-*.tar.gz" -mtime +56 -deleteSchedule it:
sudo chmod +x /usr/local/bin/backup-woocommerce.sh
sudo crontab -eAdd:
0 2 * /usr/local/bin/backup-woocommerce.sh >> /var/log/woocommerce-backup.log 2>&1Offsite replication
Local backups do not protect against drive loss or server compromise. Push daily to object storage (Backblaze B2, AWS S3, Wasabi) with rclone:
sudo apt install -y rclone
rclone config # set up remote "b2" or "s3"
rclone sync /var/backups/woocommerce b2:yourstore-backups/ --log-file=/var/log/rclone.logAdd this to the same cron, 15 minutes after the local backup.
Test restores quarterly
A backup you have never restored is a theory, not a backup. Every quarter, spin up a scratch VPS, restore the latest dump, and verify an order can be placed. See reference_dev_server.md style workflows for staging restores.
Scaling WooCommerce
Once traffic grows past what a single VPS can serve cached, you have several lever to pull before moving to multi-server.
Action Scheduler optimization
Action Scheduler is the job queue behind subscription renewals, webhook retries, email sending, and order syncs. On busy stores it becomes the bottleneck. Tune it:
- Run the dedicated cron every 1 minute (see Step 11).
- Purge completed jobs older than 30 days:
wp action-scheduler clean --batch-size=200. - For very high-volume stores, move Action Scheduler to a dedicated queue runner — a second VPS running nothing but the scheduler, connected to the same database.
Object cache scaling
When the Redis cache grows past available RAM, Redis evicts keys. Increase maxmemory in /etc/redis/redis.conf or move Redis to its own VPS. Set the eviction policy:
maxmemory 4gb
maxmemory-policy allkeys-lfuallkeys-lfu (least frequently used) is the best match for WooCommerce's access patterns.
CDN for static assets
Put Cloudflare, Bunny.net, or BunnyCDN in front of the store. Offload /wp-content/uploads/ and all static assets. Combined with FastCGI cache, this can cut origin traffic by 95%+. Configure Cloudflare page rules to bypass cache for /cart, /checkout, /my-account, and /wp-admin.
Database read replicas
For stores above 10,000 products or 1,000 orders/day, use HyperDB or LudicrousDB to split reads across replica MariaDB servers. The admin and checkout hit the primary; product catalog pages read from replicas.
Image optimization
Product images are often 70% of page weight. Install ShortPixel, Imagify, or use self-hosted Imgproxy to serve WebP/AVIF on demand. Enable lazy loading (built into WordPress core since 5.5).
Move assets to object storage
Store uploads in S3-compatible storage (Wasabi, Backblaze B2) via the WP Offload Media plugin. Frees up server disk and enables CDN without origin bandwidth cost.
When to upgrade the VPS
Watch three signals in your monitoring stack:
- CPU > 70% sustained during peak traffic -- upgrade vCPU.
- Memory > 85% used -- upgrade RAM or move Redis/MariaDB off-box.
- MariaDB slow query log growing -- tune indexes, increase
innodb_buffer_pool_size, add read replicas.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
Fatal error: Allowed memory size exhausted during checkout or admin | PHP memory_limit too low for cart/admin pages | Raise memory_limit to 512M in /etc/php/8.3/fpm/php.ini and restart PHP-FPM. In wp-config.php add define('WP_MEMORY_LIMIT', '512M'); define('WP_MAX_MEMORY_LIMIT', '768M'); |
| Stripe / PayPal payments succeed but order stays on "Pending payment" | Webhook not reaching site, or signing secret wrong | Verify endpoint URL in gateway dashboard. Re-copy the signing secret. Check WooCommerce > Status > Logs for gateway entries. Test with curl -v to the webhook URL. |
| Transactional emails not sending (order confirmation, receipts) | PHP mail() blocked by VPS provider or marked spam | Install a transactional mail plugin (WP Mail SMTP) and connect to SendGrid, Mailgun, Amazon SES, or Postmark. Never rely on default PHP mail for order receipts. |
| "Some variations failed to save" in product editor | max_input_vars too low | Set max_input_vars = 5000 in php.ini and restart PHP-FPM. |
| Cart empties when user logs in, or sessions leak between users | FastCGI cache serving cached cart/checkout | Review Nginx $skip_cache rules. /cart, /checkout, /my-account must never be cached. Verify with curl -I -- those paths should show BYPASS. |
| Product images uploaded but not showing | imagick or gd missing; Nginx blocks /wp-content/uploads/ | php -m \</td><td>grep -E 'gd\</td><td>imagick'<code>. Check uploads directory owner: </code>sudo chown -R www-data:www-data /var/www/yourstore.com/wp-content/uploads. |
| Admin dashboard extremely slow | WooCommerce Analytics regenerating data, or no object cache | Enable Redis (Step 9). Under WooCommerce > Status > Tools > Regenerate analytics, let it finish. Set define('WP_DEBUG', false); in production. |
wp-cron.php runs but scheduled jobs never fire | DISABLE_WP_CRON set but no system cron installed | Install the system cron from Step 11. Verify with sudo crontab -u www-data -l. |
Checkout throws Sorry, your session has expired | Session cookie blocked by cache or wrong site URL | Make sure siteurl and home options match exactly the HTTPS canonical URL. Disable page cache on /checkout and /cart. |
| Action Scheduler queue stuck with thousands of pending jobs | Cron not running, or PHP memory limit for CLI too low | Confirm the per-minute cron from Step 11. Set memory_limit = 512M in /etc/php/8.3/cli/php.ini. Run wp action-scheduler run --force manually to clear the backlog. |
Viewing WooCommerce logs
WooCommerce writes its own logs under /var/www/yourstore.com/wp-content/uploads/wc-logs/. View recent entries:
sudo -u www-data wp wc tool run clear_sessions --user=admin
ls -lth /var/www/yourstore.com/wp-content/uploads/wc-logs/ | headEnable verbose debugging temporarily by adding to wp-config.php:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );Logs write to /wp-content/debug.log. Disable these in production when you are done.
FAQ
Can I run WooCommerce on a 4 GB RAM VPS?
Yes, for small stores with a few hundred products and modest traffic, 4 GB of RAM is enough. You can comfortably serve 10,000-20,000 monthly visitors if you enable Redis object cache and Nginx FastCGI cache, and keep innodb_buffer_pool_size around 1 GB. The limits appear at the admin layer — bulk product edits, CSV imports over 1,000 SKUs, and the WooCommerce Analytics dashboard will feel sluggish. For serious stores, CloudCore Professional's 12 GB of RAM gives you the headroom to run Redis, MariaDB, PHP-FPM, and a full cache all at once without trading performance between them.
Do I need SSL for WooCommerce?
Yes, absolutely. WooCommerce will not let you process real card payments without HTTPS, and every modern payment gateway rejects checkout requests from non-HTTPS origins. Beyond the compliance requirement, browsers flag non-HTTPS checkout pages as insecure, which destroys conversion. Use Let's Encrypt (free, automated) or a paid certificate from your registrar. Certbot with the Nginx plugin issues and renews certificates automatically — the LEMP stack guide linked above covers this step in detail.
How do I migrate from Shopify or BigCommerce to WooCommerce?
Export products, customers, and orders as CSV from the source platform. WooCommerce's built-in CSV importer at Products > Import handles product data directly — map the columns to WooCommerce's schema (name, SKU, regular_price, sale_price, stock_quantity, categories, images). For orders and customers, use the Cart2Cart or LitExtension migration services, which handle the heavy lifting including redirects, or the WP All Import Pro plugin for custom mappings. Always migrate to a staging site first, verify data integrity, and set up 301 redirects from the old URL structure to the new one to preserve SEO.
Can WooCommerce handle subscriptions and recurring payments?
Yes, with the official WooCommerce Subscriptions extension ($239/year) or free alternatives like SUMO Subscriptions. WooCommerce Subscriptions integrates with Stripe and PayPal for automatic recurring billing, supports trial periods, sign-up fees, prorated upgrades/downgrades, and failed-payment retries via Action Scheduler. For membership sites specifically, pair it with WooCommerce Memberships to gate content and restrict product access to active subscribers.
How many products can WooCommerce handle on a single server?
With Redis object cache, FastCGI cache, and proper MariaDB tuning, a well-configured VPS easily handles 50,000+ products. The bottleneck is almost never the product count itself but rather the wp_postmeta table — WooCommerce stores product attributes, variations, and metadata as rows in this table, which can balloon to millions of rows. Add indexes on wp_postmeta.meta_key and wp_postmeta.meta_value(191), consider the WooCommerce Custom Order Tables feature (enabled by default in recent versions) which moves order data out of wp_posts into dedicated tables, and upgrade to CloudCore Professional or higher for stores past 10,000 SKUs.
Is WooCommerce PCI DSS compliant?
WooCommerce itself does not store card data — that is handled entirely by the payment gateway (Stripe, PayPal, etc.), which takes on the PCI compliance burden. As long as you use a hosted/tokenized gateway integration (the default for Stripe and PayPal on WooCommerce), your store falls under the simpler PCI DSS SAQ-A category. Keep TLS enabled everywhere, patch WordPress and plugins promptly, enforce strong admin passwords with 2FA, and you meet the baseline. If you ever process raw card numbers on your server (strongly discouraged), you enter full PCI DSS SAQ-D territory, which is a major compliance project.
Next Steps
Now that WooCommerce is running on your VPS, here are recommended next steps to grow your store:
- Install an SEO plugin -- Rank Math or Yoast SEO handle product schema, breadcrumbs, sitemaps, and meta tags. Product schema markup is critical for Google Shopping and rich snippets.
- Set up email marketing -- Install MailPoet, Klaviyo, or connect Brevo (Sendinblue) for abandoned cart emails, post-purchase flows, and broadcast campaigns. Abandoned cart recovery alone typically recovers 10-15% of lost checkouts.
- Enable analytics and tracking -- Connect Google Analytics 4 with enhanced ecommerce events, install Microsoft Clarity for session replay, and add the Meta Pixel and Google Ads conversion tags via Google Tag Manager. See How to Install Umami on Ubuntu for a privacy-friendly self-hosted alternative.
- Deploy a staging environment -- Spin up a second VPS, clone your production database and uploads, and test plugin updates and theme changes there first. Never update plugins directly on production.
- Add a monitoring and uptime stack -- Follow How to Install Uptime Kuma on Ubuntu for uptime monitoring and How to Build a Monitoring Stack on Ubuntu for Prometheus + Grafana dashboards of your server, MariaDB, and Nginx.
- Harden with a WAF -- Install CrowdSec or put your store behind Cloudflare's WAF with OWASP Core Rule Set enabled.
- Explore headless WooCommerce -- Pair the WooCommerce REST API with a Next.js or Nuxt frontend on a separate server for lightning-fast storefronts while keeping the WooCommerce admin for catalog and order management.
Skip the Manual Install — Get WooCommerce Pre-Installed>
Our Ecommerce VPS plans come with WordPress, WooCommerce, Redis, Nginx FastCGI cache, and Let's Encrypt SSL pre-configured on Ubuntu 24.04. Deploy in 60 seconds and start selling immediately.>
- WordPress 6.7 + WooCommerce latest pre-installed
- Redis object cache enabled and verified
- Nginx FastCGI cache with WooCommerce-safe rules
- PHP 8.3 tuned for WooCommerce (512M memory, 5000 input vars)
- Let's Encrypt SSL on your domain
- System cron replacing wp-cron out of the box>
Deploy Your WooCommerce VPS Now — Plans start at EUR 19.99/month on CloudCore Professional.