Skip to content

Provisioning a Production-Ready WordPress Server in 30 Minutes with Trellis

Emnes
Server rack illustration showing nginx, php-fpm, mariadb, redis, fail2ban, and letsencrypt services

Setting up a production-ready WordPress server used to be a weekend project. You’d SSH into a fresh VPS, install nginx, configure PHP-FPM, set up MariaDB, add Let’s Encrypt certificates, configure fail2ban, enable a firewall, tune caching, configure automatic backups, and then — if you were diligent — document the whole process so you could repeat it next time. If you weren’t diligent, you’d rebuild the runbook from memory on every new server, slowly drifting over time.

With Trellis, provisioning a production-grade WordPress server takes about 30 minutes end-to-end, most of which is the server doing work while you watch. Every setting is declarative, version-controlled, and reproducible. You can provision one server today and ten identical servers next year without remembering a single manual step.

This guide walks through provisioning a production WordPress server with Trellis from scratch: picking a VPS provider, configuring Trellis, running the provisioning playbook, deploying a site, and verifying everything is secure and tuned. By the end, you’ll have a production-ready server and know exactly what’s on it and why.

What Trellis Provisions

A Trellis-provisioned server is a complete, hardened WordPress LEMP stack. Out of the box, you get:

  • Ubuntu 24.04 LTS with all packages updated and security patches applied.
  • Nginx with HTTP/2, HTTP/3, OCSP stapling, HSTS, strong TLS ciphers, and FastCGI micro-caching.
  • PHP 8.3 with OPcache tuned for WordPress performance.
  • MariaDB with sensible InnoDB settings.
  • Redis for object caching and PHP session storage.
  • Memcached as an optional secondary cache.
  • Let’s Encrypt SSL certificates with automatic renewal.
  • msmtp for outbound mail via SMTP.
  • fail2ban monitoring SSH and nginx logs for brute force attempts.
  • UFW firewall configured to allow only HTTP, HTTPS, and SSH.
  • Automatic package updates for security patches.
  • WP-CLI installed system-wide.
  • Composer installed system-wide with plugin authentication support.
  • Dedicated web user with SSH key authentication; root login disabled.

Every single component is configured through Ansible roles. You can read the source, modify the defaults, or keep everything stock — all decisions are version-controlled in your Trellis repo.

Before You Start

You’ll need:

  • A VPS — DigitalOcean, Hetzner, Linode, Vultr, AWS Lightsail, or similar. 2 GB RAM minimum for a single WordPress site; 4 GB recommended for comfort and multiple sites.
  • A domain name with DNS you can control (to point A records at the server).
  • An SSH public key on your local machine.
  • A new or existing Trellis project. If you don’t have one, trellis new my-project scaffolds a fresh Bedrock + Trellis setup.
  • Ansible installed locally (Trellis-CLI handles this automatically for most cases).

If you’ve followed our Roots.io stack overview and installed trellis-cli, you’re set.

Step 1 — Provision the VPS

Create the VPS on your provider of choice:

  • OS: Ubuntu 24.04 LTS (Trellis targets this version specifically).
  • Size: at least 2 GB RAM, 50 GB SSD.
  • Region: closest to your users.
  • SSH key: add your public key so Trellis can connect.

Note the server’s public IP — you’ll need it shortly.

Shortcut: Trellis-CLI 1.17+ supports pluggable cloud providers. If you’re on Hetzner or DigitalOcean, trellis droplet create production or trellis hetzner create production will provision the VPS and add it to your Ansible inventory in one step.

Step 2 — Configure the Trellis Inventory

Edit trellis/hosts/production with the VPS IP:

[production]
your.server.ip ansible_host=your.server.ip

[web:children]
production

Edit trellis/group_vars/production/wordpress_sites.yml with your site definition:

wordpress_sites:
  example.com:
    site_hosts:
      - canonical: example.com
        redirects:
          - www.example.com
    local_path: ../site
    repo: [email protected]:your-org/your-repo.git
    branch: main
    multisite:
      enabled: false
    ssl:
      enabled: true
      provider: letsencrypt
    cache:
      enabled: true

Step 3 — Set Up Vault Secrets

Production credentials live in trellis/group_vars/production/vault.yml, encrypted with Ansible Vault. Create a .vault_pass file in the Trellis directory with a strong password (never commit this file), then encrypt the vault:

ansible-vault encrypt group_vars/production/vault.yml

Populate the vault with:

  • Database root password
  • Per-site database passwords
  • WordPress auth keys and salts
  • SMTP credentials for outbound mail
  • Any other production-only secrets

Step 4 — DNS Setup

Before provisioning, point your domain’s A record at the VPS IP. SSL cert generation requires the domain to resolve correctly, so this needs to happen first.

For most registrars, this is a 5-minute task. Wait 10–15 minutes for propagation and verify with dig example.com A.

Step 5 — Run the Provision Playbook

From the Trellis directory:

trellis provision production

Ansible runs through dozens of tasks:

  • Updates the OS package cache.
  • Installs base packages (build tools, Python, Git, curl).
  • Creates the web user and adds your SSH key.
  • Disables root login, enforces SSH key auth.
  • Configures UFW firewall.
  • Installs and configures MariaDB.
  • Installs and configures PHP 8.3 + PHP-FPM.
  • Installs and configures nginx with HTTP/2, HTTP/3, HSTS.
  • Installs and configures Redis and Memcached.
  • Installs WP-CLI and Composer.
  • Configures fail2ban monitoring SSH and nginx.
  • Generates Let’s Encrypt certificates for each site.
  • Configures msmtp for outbound mail.
  • Sets up the directory structure for each site.
  • Enables automatic security updates.

Total time: 20–35 minutes depending on server speed and connection. You can watch the Ansible output scroll by, or leave it running and come back.

When provisioning completes, the server is ready to accept deploys. Nginx is running, PHP-FPM is running, MariaDB is running, SSL certs are issued.

Step 6 — First Deploy

With the server provisioned, deploy your Bedrock site:

trellis deploy production example.com

Trellis clones your Git repo into a timestamped release directory, runs Composer install, symlinks shared files, and flips the current symlink. (See our zero-downtime deploys post for the detailed workflow.)

Visit your domain. You should see the WordPress installer prompting for site title and admin credentials. Complete the install and you have a working production WordPress site.

What the Server Looks Like When Done

SSH into the server (ssh [email protected]) and poke around:

  • /srv/www/example.com/ — your site’s release directories, shared files, and current symlink.
  • /etc/nginx/sites-available/example.com.conf — the per-site nginx config.
  • /etc/php/8.3/fpm/pool.d/example.com.conf — the per-site PHP-FPM pool.
  • /etc/letsencrypt/live/example.com/ — SSL certificates.
  • /var/log/nginx/example.com.access.log — per-site nginx access log.
  • /var/log/nginx/error.log — nginx error log (server-wide).

Everything is laid out predictably. If you’ve SSHed into a Trellis server before, you know where to find things on any other Trellis server.

Verifying the Security Posture

After provisioning, run a quick security audit to verify the defaults:

  • SSH: ssh -v [email protected] should fail (root login disabled). ssh [email protected] should succeed.
  • Firewall: sudo ufw status should show ports 22, 80, 443 allowed and everything else denied.
  • SSL: ssllabs.com/ssltest should rate your site A or A+.
  • Security headers: securityheaders.com should show HSTS, X-Content-Type-Options, Referrer-Policy, etc.
  • fail2ban: sudo fail2ban-client status should show active jails for sshd and nginx-http-auth.

Trellis’s defaults pass all of these out of the box. If any are failing, check the Ansible logs for hints about what failed during provisioning.

Day-Two Operations

After the initial provisioning, day-two tasks are simple:

  • Add a new site — add it to wordpress_sites.yml, re-run trellis provision production, deploy.
  • Update server packages — automatic security updates handle this. Run trellis provision periodically to pull in larger Ansible role updates.
  • Rotate SSL certificates — handled automatically by certbot.
  • Backup — Trellis doesn’t include backup out of the box. Add your own — our Backup Migrate Reset plugin, plus a cron job that rsyncs shared/uploads/ off-site.
  • Monitor — add a monitoring service (UptimeRobot, Pingdom) and server-side monitoring (Netdata, Prometheus).

Frequently Asked Questions

What VPS size do I need?

For a single low-traffic site: 2 GB RAM is enough. For multiple sites or moderate traffic: 4 GB. For high-traffic or WooCommerce: 8 GB+. CPU matters less than RAM for most WordPress workloads.

Can I add a site after provisioning?

Yes. Add the new site to wordpress_sites.yml, run trellis provision production again. Trellis is idempotent — re-provisioning an existing server only applies changes, not from-scratch work.

Does Trellis set up automatic backups?

No. Backup strategy is left to the operator — databases, uploads, and any custom data you want preserved. Use a WordPress backup plugin for database backups and a scheduled rsync job for uploads.

Can I run non-WordPress sites on a Trellis server?

Technically yes (it’s just Ubuntu), but Trellis’s tooling assumes WordPress. For a mixed-workload server, keep Trellis for the WordPress sites and manage non-WordPress sites with a separate Ansible role.

How do I scale beyond one server?

Trellis targets single-server setups. For multi-server architecture (separate database server, load-balanced web tier, shared media storage), you’re building custom infrastructure — Trellis can provision each server individually, but coordinating them is on you.

Can I migrate an existing WordPress server to Trellis?

Not in place. Provision a fresh Trellis server, migrate your site to it (possibly converting to Bedrock in the process — see our Bedrock migration guide), switch DNS, retire the old server.

Production-Ready, Reproducible, Version-Controlled

The best thing about Trellis provisioning isn’t that it’s fast — although 30 minutes vs a weekend is nice. It’s that the whole setup is code. Your server configuration is in Git, versioned alongside your site. Six months from now when you need to spin up a staging server, or a year from now when you migrate to a new provider, the exact same configuration provisions an identical server. No memory required.

At Emnes, we manage 18 production WordPress sites across two Trellis-provisioned servers. When we added the second server, the provisioning took less than an hour because everything was already defined — we just changed the inventory and ran provision. That’s the magic of infrastructure-as-code.

Related reading: Trellis zero-downtime deployments, Vagrant to Lima, and the Bedrock complete guide.