How to deploy & self-host WooCommerce on DigitalOcean

How to deploy & self-host WooCommerce on DigitalOcean

We may earn an affiliate commission through purchases made from our guides and tutorials.

Here’s a straight-shooting, battle-tested walkthrough for getting a WooCommerce store running on a DigitalOcean VPS (Droplet). We’ll build on Ubuntu 24.04 LTS with Nginx, PHP-FPM, and MySQL (or DigitalOcean Managed MySQL if you’d rather offload the database). You’ll secure it with Let’s Encrypt, harden the basics, and set the right cache rules so carts and checkouts don’t break.

Quick note before we roll: you’ll need a DigitalOcean account to create a Droplet. If you don’t have one yet, sort that out first.

WooCommerce is the e-commerce plugin that turned WordPress into a store builder back in 2011 (born at WooThemes; Automattic acquired it in 2015). It’s popular for a reason: you keep control, extend it as you like, and you’re not locked into monthly platform fees. That also means you own the responsibility to host it properly—fast, secure, with sane caching and reliable background tasks.

Prereqs and versions that actually matter

  • WordPress runs best today on PHP 8.3+, with MySQL 8.0+ (or MariaDB 10.6+), and HTTPS. Apache or Nginx both work; we’ll use Nginx.
  • WooCommerce’s own server notes echo the same themes: modern PHP, MySQL/MariaDB, memory for PHP, and HTTPS. Don’t cheap out there.
  • Ubuntu 24.04 LTS is a good base and will be supported for years.

If you prefer a faster start, DigitalOcean’s WordPress Marketplace image auto-provisions WordPress on first login; you can then just add WooCommerce. We’ll do the manual build so you understand every piece, but that 1-Click is a real option.

Create the Droplet, log in, and get the basics right

Choose Ubuntu 24.04 LTS, pick a plan that fits, add your SSH key, and create. When it’s up, SSH in as root.

Create a non-root sudo user and lock down SSH a bit:

adduser deploy
usermod -aG sudo deploy
rsync -a ~/.ssh /home/deploy/
chown -R deploy:deploy /home/deploy/.ssh

Enable the uncomplicated firewall (UFW) and allow Nginx + SSH:

apt update && apt -y upgrade
apt -y install ufw
ufw allow OpenSSH
ufw allow 'Nginx Full'
ufw --force enable
ufw status verbose

Install Nginx, PHP-FPM 8.3, and MySQL (or point to Managed MySQL)

Install the core stack:

apt -y install nginx php8.3-fpm php8.3-cli php8.3-mysql php8.3-curl php8.3-xml php8.3-zip php8.3-mbstring php8.3-intl php8.3-gd php8.3-imagick php8.3-opcache mariadb-server

Notes:

  • Those PHP extensions cover typical WordPress/WooCommerce needs (mbstring, intl, gd/imagick, curl, xml, zip, opcache). Imagick isn’t strictly required but helps with image quality.
  • If you prefer DigitalOcean Managed MySQL, skip installing MariaDB and provision a Managed DB, then connect WordPress to it. It’s a paid but maintenance-free option and scales easily.

For a local MariaDB, secure it and create a database/user:

mysql_secure_installation
mysql -u root -p -e "CREATE DATABASE shopdb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci; \
CREATE USER 'shopuser'@'localhost' IDENTIFIED BY 'REPLACE_ME_STRONG'; \
GRANT ALL PRIVILEGES ON shopdb.* TO 'shopuser'@'localhost'; FLUSH PRIVILEGES;"

PHP-FPM and OPcache tuning (sane defaults)

Open /etc/php/8.3/fpm/php.ini and confirm/adjust:

memory_limit = 256M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 120

opcache.enable=1
opcache.memory_consumption=192
opcache.max_accelerated_files=100000

Then:

systemctl reload php8.3-fpm

(WordPress recommends modern PHP; OPcache is a free speed boost.)

Fetch WordPress in a clean web root and set permissions

We’ll use /var/www/shop:

mkdir -p /var/www/shop
cd /var/www/shop
curl -O https://wordpress.org/latest.tar.gz
tar -xzf latest.tar.gz --strip-components=1
rm latest.tar.gz

Create a WordPress config connected to your DB:

cp wp-config-sample.php wp-config.php
sed -i "s/database_name_here/shopdb/; s/username_here/shopuser/; s/password_here/REPLACE_ME_STRONG/" wp-config.php

Generate and set unique AUTH/SALT keys:

curl -s https://api.wordpress.org/secret-key/1.1/salt/ | awk '{printf "define(%s %s);\n",$1,$2}' >> wp-config.php

Lock ownership to the PHP user:

chown -R www-data:www-data /var/www/shop
find /var/www/shop -type d -exec chmod 755 {} \;
find /var/www/shop -type f -exec chmod 644 {} \;

Nginx server block (with real WordPress rewrites)

Create /etc/nginx/sites-available/shop:

server {
    server_name yourdomain.com www.yourdomain.com;
    root /var/www/shop;
    index index.php;

    # Basic security headers (adjust CSP to your stack)
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;
    add_header Referrer-Policy strict-origin-when-cross-origin;

    # WordPress front controller
    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    # PHP-FPM
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }

    # Deny access to sensitive files
    location ~* \.(ini|log|sh|sql)$ { deny all; }
    location ~* /(\.git|\.env) { deny all; }

    # Media: serve directly
    location ~* \.(jpg|jpeg|png|gif|webp|svg|css|js|ico)$ {
        try_files $uri =404;
        access_log off;
        expires max;
    }
}

Enable and test:

ln -s /etc/nginx/sites-available/shop /etc/nginx/sites-enabled/shop
nginx -t && systemctl reload nginx

(WordPress on Nginx needs explicit rewrites; there’s no .htaccess like Apache. Above try_files handles “pretty permalinks.”)

Point DNS and get HTTPS via Let’s Encrypt (auto-renew)

Set your domain’s A/AAAA records to the Droplet IP, then:

apt -y install certbot python3-certbot-nginx
certbot --nginx -d yourdomain.com -d www.yourdomain.com --redirect -m you@example.com --agree-tos -n

Certbot installs, gets a cert, and wires auto-renew cron/systemd timers.

Finish WordPress install, then add WooCommerce

Visit https://yourdomain.com and complete the WordPress installer. Once you’re in the dashboard, install WooCommerce:

# Optional: with WP-CLI if you like working from shell
cd /var/www/shop
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
php wp-cli.phar --info
chmod +x wp-cli.phar && mv wp-cli.phar /usr/local/bin/wp

sudo -u www-data wp plugin install woocommerce --activate --path=/var/www/shop
sudo -u www-data wp theme install storefront --activate --path=/var/www/shop

WooCommerce will take you through the onboarding wizard (payments, shipping, taxes). The plugin itself is the official e-commerce layer for WordPress.

Critical WooCommerce performance/caching rules (don’t break carts)

You must not page-cache dynamic commerce flows (Cart, Checkout, My Account). If you enable a cache plugin or put a CDN in front, exclude those endpoints so your customers don’t see stale carts or wrong totals. WooCommerce’s own guidance calls this out plainly.

If you deploy Nginx micro-caching or a reverse proxy/CDN, add bypass rules for:

  • /cart*, /checkout*, /my-account*, /wc-api/*, and any payment/thank-you webhooks.

For object caching, Redis is excellent for WooCommerce (faster queries, fewer DB trips). Use a Redis object cache plugin—or DigitalOcean’s Managed Redis/Valkey if you want externalized cache.

Example (bypass with X-Bypass-Cache header if you’re doing custom caching):

set $bypass_cache 0;
if ($request_uri ~* "^/(cart|checkout|my-account|wc-api)/") {
    set $bypass_cache 1;
}
add_header X-Bypass-Cache $bypass_cache always;

Background jobs that actually run (orders, emails, webhooks)

WooCommerce leans on Action Scheduler (bundled) to process queued tasks—emails, webhooks, subscription renewals, etc. Don’t rely solely on WP-Cron’s “visit-based” trigger. Add a real system cron to hit wp-cron.php regularly or better: disable WP-Cron and call it via cron.

Disable built-in pseudo cron:

echo "define('DISABLE_WP_CRON', true);" >> /var/www/shop/wp-config.php

Create a cron:

crontab -u www-data -e

Add:

* * * * * /usr/bin/php /var/www/shop/wp-cron.php > /dev/null 2>&1

(Every minute keeps the queue moving; scale timing as your order volume dictates.)

Permalinks, uploads, and the “it just works” checklist

  • In Settings → Permalinks, pick “Post name.” With our Nginx try_files, pretty URLs work out of the box.
  • Ensure the uploads directory is writable by www-data.
  • Install a cache plugin (WP Rocket, etc.) if you want page caching for public pages—but never cache cart/checkout/account. Many hosts/plugins exclude these automatically, but verify.

Optional but smart: Move media and scale the right way

When your catalog grows, local disk becomes a bottleneck for backups and deploys. Consider moving media to DigitalOcean Spaces (S3-compatible) and enable the built-in CDN. It’s inexpensive and removes media I/O from the Droplet.

At higher traffic:

  • Use DigitalOcean Managed MySQL so DB replication/patching aren’t your problem.
  • Put two web Droplets behind a DigitalOcean Load Balancer; keep sessions stateless (login/carts handled by WooCommerce cookies and AJAX). Sticky sessions can help if you use certain plugins.

Minimal security hygiene that pays off

  • Keep Ubuntu and packages updated; schedule a weekly maintenance window.
  • Fail2ban is worth enabling for sshd and, if you’re ambitious, for Nginx auth and WordPress XML-RPC/login heuristics.
  • Use strong API/webhook secrets for payment gateways and fulfillments.
  • Backups: snapshot the Droplet, dump the DB nightly, and—if using Spaces—version your buckets.

Quick sanity test

  • Visit the homepage over HTTPS.
  • Switch permalinks; visit a product page.
  • Add to cart → view cart → checkout.
  • Trigger a test order on a sandbox payment gateway.
  • Check WooCommerce → StatusScheduled Actions and confirm they process.

WP-CLI cheat sheet (handy once you’re live)

# Core updates
sudo -u www-data wp core update --path=/var/www/shop
sudo -u www-data wp plugin update --all --path=/var/www/shop
sudo -u www-data wp theme update --all --path=/var/www/shop

# Object cache (if using Redis plugin)
sudo -u www-data wp plugin install redis-cache --activate --path=/var/www/shop
sudo -u www-data wp redis enable --path=/var/www/shop

Troubleshooting the usual suspects

  • Permalinks 404s → Your Nginx try_files isn’t loaded or wrong server block is active. Test with nginx -t and ensure the shop site is enabled.
  • Mixed content after HTTPS → Force HTTPS in WordPress Settings → General, and consider a search/replace of HTTP URLs if you migrated data.
  • Cart/Checkout acting weird → Something is caching them. Exclude those paths in your cache/CDN, and clear all layers.
  • Orders/emails delayed → Cron not running. Verify the system cron and Action Scheduler queue.

If you start from the DigitalOcean WordPress 1-Click image, the setup script guides initial hardening and installs common tooling (UFW, Fail2ban). After DNS + HTTPS, jump straight to WooCommerce, caching exclusions, and cron setup from the steps above.

That’s it. You’ve got a clean WooCommerce install on a tight LEMP stack, HTTPS by default, caching that won’t bite you, real cron for background jobs, and a straight path to scale with Spaces, Managed MySQL, and a load balancer when it’s time. If you want me to tailor the Nginx or PHP-FPM settings to your exact catalog size and traffic profile, let me know in the comments and I can prepare you a config file.

Was this helpful?

Thanks for your feedback!
Alex is the resident editor and oversees all of the guides published. His past work and experience include Colorlib, Stack Diary, Hostvix, and working with a number of editorial publications. He has been wrangling code and publishing his findings about it since the early 2000s.

Leave a comment

Your email address will not be published. Required fields are marked *