Home > Control panel > Your Woop! Host stack

Your site. Your tools. A managed stack underneath.

Know what runs your site
and how to get the best from it.

example.com runs on its own virtual machine. You get WordPress, SSH, SFTP, WP-CLI and database access. We manage the web, database, caching, certificate, backup and monitoring layers.

Woop! Host at a glance

  • 01One site per virtual machine
  • 02Daily backup before updates
  • 03Layered page and object caching
  • 04Real shell and file access
About this guide This page describes the latest Woop! Host stack. A platform repository refresh alone does not update an installed site. Use Reinstall software stack in your hosting panel to add the current stack features to an older site. The reinstall keeps your site content and attempts a protected backup first. If that backup fails, the reinstall continues with a warning. Save your work first: the reinstall disconnects SSH, SFTP and Resident Engineer sessions. See the recent changes.

Choose the way you work

Your hosting panel, then four ways into the site

The panel shows the platform around WordPress. Use the four site tools for publishing, files, commands and engineering work.

Your site control panel

See and manage the whole site from one page.

Open status, access, troubleshooting and lifecycle controls for example.com.

Open the hosting panel
  • Domains and DNSValidate DNS, change the site domain, or use the permanent site-xxxx.woop.host address.
  • Site accessReset the administrator password or create a separate temporary administrator for 24 hours.
  • Browser troubleshootingRead recent NGINX, PHP-FPM and WordPress error logs without signing in to the server.
  • Site facts and controlsSee PHP, disk use, stack version and data centre. Refresh status, restart services or reinstall the stack.
01

WordPress

Publish, design and manage plugins through the familiar wp-admin screens.

Open wp-admin on your site
02

SSH and WP-CLI

Run updates, inspect the site and make precise changes as the owner of the files.

See shell access
04

Resident Engineer

Choose Claude Code or Codex, then ask in plain English. Both know this stack and use the same playbooks for safe changes and troubleshooting.

Meet the engineer

Under the hood

A fast path in. Clear boundaries all the way down.

A normal page request moves through specialist layers. Each layer has one job and only the access it needs.

Visitor Browser HTTPS request
Secure edge NGINX + TLS Encryption and static files
Page cache Varnish Fast anonymous visits
Application PHP-FPM Runs WordPress + microcache
Site WordPress Themes, plugins and content
Data MariaDB + Redis Durable data and object cache
Fast

Cache without stale admin work

Varnish serves anonymous visits quickly. Logged-in sessions, carts and wp-admin bypass page caching. WordPress purges cached pages when content changes.

Isolated

One customer, one machine

Your site does not share a filesystem, database or traffic budget with another customer's WordPress installation.

Contained

Access stops at your site

Your shell is powerful inside the site and deliberately cannot manage the operating system, services or another account.

Managed

The infrastructure is our job

We maintain PHP, the database, caching, certificates, monitoring, backups and system security updates.

Part one

Work with your site

Your website is ready to use. These are tools, not setup steps you have missed.

SSH access and WP-CLI

A real shell, already pointed at your WordPress installation.

ssh wpuser@example.com

You see your site and your own files. The shell is jailed, so you cannot see or change the web server, database service, caches, certificates or anyone else's processes.

Your site is still yours, including the right to break it. You can edit, overwrite and delete files you own. Backups and the Resident Engineer give you practical ways back if a change goes wrong.

Available in your shell

wp, php, mysql, mysqldump, git, svn, composer, curl, rsync, tar, zip, editors, tmux and image tools.

Managed by us

System packages, services, PHP versions, database configuration, TLS, caching and operating-system security.

WP-CLI already knows the site path. You do not need --path:

wp plugin list wp theme install some-theme.zip --activate wp search-replace 'http://old' 'https://new' --dry-run

WP-CLI runs as the owner of the files. It can install themes and plugins without granting web code write access to PHP.

SFTP — secure file transfer

Use SFTP over port 22. Do not select FTP or FTPS in your client.

ProtocolSFTP / SSH File Transfer
Port22
Usernamewpuser
Website/var/www/wordpress

You start in your home directory. Files arrive owned by you. Some clients, archives and rsync commands can give uploaded files the wrong group. If WordPress then says a theme or plugin is missing, use the permissions repair.

Database access

Use Adminer for quick browser work, or tunnel a desktop client over SSH.

Browser

Adminer

example.com/db

Useful for a quick look, export or migration import.

Desktop client

SSH tunnel

ssh -N -L 3307:localhost:3306 wpuser@example.com

Connect the client to 127.0.0.1:3307. Database credentials are in wp-config.php.

The database is not open to the internet. From your site shell, run mysql to connect over the local socket.

The Resident Engineer

Claude Code and Codex are WordPress specialists that already know this stack and can inspect, back up, change and verify your site.

Start a persistent customer session directly from your computer:

ssh -t wpuser@example.com claude ssh -t wpuser@example.com codex

If you are already connected over SSH, run claude or codex. Both clients run as wpuser, so their operating boundary is the same as yours.

Describe the outcome in ordinary language, or invoke a bundled skill for a particular playbook. In Claude Code, use /wphost-backup. In Codex, use $wphost-backup. Run /skills in Claude Code to see the complete current list.

Understand and diagnose

wphost-site-report · wphost-troubleshoot

Inventory the site and pending updates, or work methodically through errors, outages and plugin conflicts.

Change safely

wphost-backup · wphost-safe-update

Create a protected restore point, then update one component at a time and check the site after each change.

Review site health

wphost-performance-audit · wphost-security-audit

Find database, media and caching bottlenecks, or produce a read-only integrity and security report.

Move a site

wphost-migrate-export · wphost-migrate-import

Build and verify a migration bundle, transfer it safely, import it, repair permissions and check the result.

Build and edit

wphost-child-theme · wphost-content-ops

Keep theme customizations update-safe, or make reviewed bulk content and URL changes with a dry run first.

Verify and recover

wphost-visual-check · wphost-exploit-recovery

Check responsive rendering in managed Chromium, or investigate a suspected compromise before cleanup.

Sign in with your own supported Claude, ChatGPT or API account. Your persistent login and local session history stay in your server home directory. Relevant file content and command output go to your selected model provider. Do not steer either client at credentials, customer records or secrets.

Guest sign-in. Plain claude-guest starts Claude Code's sign-in. Follow the steps shown in the SSH terminal. Plain codex-guest starts ChatGPT device login. Open the displayed URL on your own computer and enter the one-time code. Neither default requires Claude Code or Codex on your computer. Use ssh -t for an interactive remote session.

If you use --prompt-token for Claude, create a setup token on your own trusted computer with claude setup-token. Do not create it from the client's persistent Claude login.

To use an API key, ask the remote command to read it without echo:

ssh -t wpuser@example.com claude-guest --prompt-api-key ssh -t wpuser@example.com codex-guest --prompt-api-key

Use --prompt-token instead for a Claude setup token or Codex access token. Do not put the credential in the SSH command. A variable before ssh remains local and does not normally reach the guest command.

Codex may warn that it cannot create PATH aliases in its temporary directory. The warning is expected because the managed codex command is already available. Run claude-guest --help or codex-guest --help for all options.

It is an engineer, not an oracle. Read proposed destructive actions before approving deletion, user changes or publishing. The platform guidance asks it to back up before risky work and verify the site afterwards.

Moving an existing site in

Bring files, import the database, replace the old URL, then verify the result.

Keep your existing site running while you prepare the new copy. If its domain still points to the old host, use a temporary Woop! Host domain from your hosting panel to test the new site before changing DNS. Take a backup before importing over any content on the destination.

  1. 1
    Move the files

    Use SFTP, rsync or SSH from the old host.

  2. 2
    Import the database

    Use Adminer, mysql or WP-CLI.

  3. 3
    Update URLs safely

    Run wp search-replace with --dry-run first.

  4. 4
    Check permissions and behavior

    Repair ownership, then test public pages and wp-admin.

For a Woop!-to-Woop! move, the two Resident Engineers can coordinate the transfer after you approve it. The destination creates restricted, expiring SSH trust. The source builds a manifest and bundle. The destination then pulls, verifies, imports and checks the site. Private keys never move between the sites.

See a worked example: a WordPress migration with two Resident Engineers. The post follows the transfer and checks, including the decisions the site owner approved. For the current migration skill names, see the Resident Engineer section above.

Woop! Host also specialises in migrations from exploited sites. The security and exploit-recovery skills inspect the imported copy, replace compromised software and verify the site before it goes live on the current stack.

Part two

Care and maintenance

The routine server work is automated. These sections show where you still make the decisions.

Backups and restores

Protected daily history plus self-service backup, comparison, staging, export and guarded live restore.

DailyAutomatic backup cadence
~30 daysNormal history
10 daysMinimum history for large backups
Read-onlySite access to managed backups

Extra backups are attempted before a stack reinstall, before risky Resident Engineer work, and whenever you or we request one. Managed history lives outside the website folder, where WordPress cannot alter or delete it.

Database backups exclude disposable Action Scheduler logs. The scheduled actions remain in the backup.

The helper is installed at /usr/local/bin/wphost-backups.sh and is also available by its short name. Start by listing recent runs. Each successful run has a stable run ID:

wphost-backups.sh info wphost-backups.sh list --limit 10
Take a backup now

Create a named restore point

wphost-backups.sh backup --reason "before plugin change"

The request waits for the protected files-and-database backup to finish. If an interrupted command printed a request-... ID, check the same request with wphost-backups.sh request-status REQUEST-ID. A rate-limited result gives the UTC retry_at time.

See what changed

Compare a run with the current site

wphost-backups.sh diff --run RUN-ID --names

Scope the comparison with --plugin, --theme, --path, --uploads, --db or --table.

Inspect safely

Restore into your home directory

wphost-backups.sh restore --run RUN-ID --plugin plugin-name

Staging is the default. The live site is unchanged and the restored content lands under ~/restores.

Keep or download a copy

Export files and database

wphost-backups.sh export --run RUN-ID

The portable archive lands under ~/backups/wphost/exports and uses your disk space.

Restore the live site

Use the run ID from list throughout the review and restore. Preview a full files-and-database restore first:

wphost-backups.sh restore --run RUN-ID --live --exact --dry-run # After you review the dry run: wphost-backups.sh restore --run RUN-ID --live --exact --yes

Use --merge to keep files created after the backup, or --exact to make the selected file scope match history. Instead of a run ID, --at-or-before 7D selects by time and --with-reason "label" selects the newest exact reason match. A bare date means midnight at the start of that date.

Automatic updates

The Auto-updates switch beside each plugin and theme controls whether the daily pass updates that item.

  1. 01
    Back up files and database

    A restore point exists before anything changes.

  2. 02
    Update WordPress core

    Core is brought up to the current release.

  3. 03
    Update opted-in plugins and themes

    Only items with WordPress auto-update enabled are included.

  4. 04
    Repair file permissions

    The stack checks ownership after the update work.

If the daily backup fails, WordPress core, plugin and theme updates are skipped. Permission repair and other routine maintenance still run.

Plugins and themes present when a site is built normally start with auto-update enabled. A plugin installed later follows WordPress's default, which is off. Check the Plugins screen after adding one.

Scheduled posts, WooCommerce jobs and plugin cron run from the server every five minutes. They do not depend on a visitor arriving at the right moment.

Email from your site

Use local Postfix by default, or configure an SMTP provider.

Which email address does what?

Address or namePurpose and usual setting
From email and nameThe sender shown in the inbox, such as Example Website <bookings@example.com>. WordPress defaults to WordPress <wordpress@example.com> for a site at example.com, with a leading www. removed. A form, shop or mail plugin can replace these values.
Reply-ToWhere a reader's reply goes. Contact forms commonly use the visitor's address here and an address you control as From. In Contact Form 7, use the Mail tab's From field and Additional headers for Reply-To. No WPHOST_SMTP_* setting controls Reply-To.
Envelope sender / Return-PathWhere delivery failures go. SPF checks this address's domain. Mailer settings may call it Sender or Return Path. It can differ from From; changing From alone does not necessarily change it.
SMTP usernameThe login to a mail provider, not necessarily an email address. It is separate from the sender; SendGrid's login, for example, is apikey.

The WordPress administration email does not automatically become the sender. Forms and mail plugins can set From and Reply-To without changing the delivery route.

Mail tracing

For each attempt and its result, the stack records the time, hostname, recipients, the first 200 characters of the subject, headers, attachment count and the PHP call chain that led to the send. Message bodies are never recorded.

The log is outside the website and survives a site's software stack reinstall. It identifies the plugin and function behind unexpected mail. Disable all tracing with WPHOST_MAIL_TRACE, or only the call chain with WPHOST_MAIL_TRACE_BACKTRACE.

Local delivery through Postfix

No provider account is needed, and Postfix queues and retries mail. Delivery depends on the VM's reputation and your domain's authentication.

No SMTP constants are needed. WordPress submits mail through PHP's mail() function to local Postfix. Postfix normally sends over IPv4 from the VM to recipients' mail servers, announcing the permanent site-xxxx.woop.host alias. A configured server-level relay changes that route.

An unset bounce address normally comes from the account running PHP at the VM's permanent alias. Web requests, scheduled WordPress jobs and WP-CLI can therefore have different bounce addresses. To choose From and the bounce address together, use the local Postfix SMTP example below.

DNS for the default route

  • Woop! alias: We publish v=spf1 a -all for the permanent site-xxxx.woop.host alias. This authorises that alias's IP to send mail with a bounce address at the alias. You do not edit this DNS record.
  • Your own domain: If the bounce address uses your domain, authorise the actual sending IP in that domain's existing SPF record. For direct delivery, add a:site-xxxx.woop.host before the final all rule, using your actual permanent site alias. SPF then looks up the alias's address, so you do not hard-code the VM's IP. Preserve other sending services and keep one SPF record per domain. If Postfix uses a relay, use that relay's SPF instructions instead.
  • Incoming mail: Keep your existing MX records. This outgoing-mail setup does not create customer mailboxes.

SPF for the Woop! alias does not by itself satisfy DMARC for From: bookings@example.com. DMARC needs a passing SPF result with an aligned bounce domain, or a passing DKIM signature aligned with the From domain. Adding SPF to the visible From domain alone does not fix a bounce address at an unrelated Woop! alias.

Using the same domain for From and the bounce address is the simplest way to align SPF. A dedicated bounce subdomain needs its own SPF record; the parent domain's SPF is not inherited. bounce.example.com can align with example.com under relaxed DMARC SPF alignment, but strict alignment requires identical domains. Keep your existing policy and choose addresses that meet it.

The stack installer does not set up DKIM signing for your own domain. Publishing a DKIM DNS record alone does not sign messages. For DKIM signing through a provider, use an SMTP provider and complete its sender verification and DNS setup. Keep your existing DMARC policy.

Support can check the VM's reverse DNS, hostname, outbound TLS, queue failures and IP reputation. Local DKIM signing requires a server-side signer and matching DNS record.

After setup, send a real site message. Check its From, Return-Path and Authentication-Results headers. A successful WordPress send can mean only that local Postfix accepted the message; it does not prove inbox delivery. Ask support to check the installed Postfix route if delivery fails.

SMTP settings and providers

A provider can handle DKIM signing and sending reputation, but requires an account and may impose sending limits.

The Woop! Host plugin reads WPHOST_SMTP_* settings from wp-config.php. An external SMTP provider bypasses Postfix's queue; retries depend on the calling plugin. The local Postfix example below keeps that queue.

To use your own SMTP provider, add these three constants to /var/www/wordpress/wp-config.php over SSH or SFTP. Place them before the “That's all, stop editing!” line and before WordPress loads wp-settings.php. If an SMTP constant already exists, edit it instead of adding a duplicate.

define( 'WPHOST_SMTP_HOST', 'smtp.example.com' ); define( 'WPHOST_SMTP_USER', 'wordpress@example.com' ); define( 'WPHOST_SMTP_PASS', 'your-smtp-password' );

The host enables SMTP. Credentials enable authentication, port 587 and required STARTTLS, which encrypts the connection. If the username is an email address, that address becomes both the From address and bounce address. Otherwise, WordPress keeps the sender supplied by your site or form plugin. The existing display name stays unchanged.

Use a hostname or IP address without a URL scheme or port in WPHOST_SMTP_HOST. All credentials shown here are placeholders. Enter your credentials only in your site's configuration. Never paste passwords or API keys into a support ticket. The Resident Engineer can check settings without displaying credentials.

Relays and port 465

For a relay that accepts mail without a login, only the host is required:

define( 'WPHOST_SMTP_HOST', 'relay.example.com' );

This uses port 25 without authentication and upgrades to STARTTLS when the server offers it. The relay must permit sending from your VM. If old credentials remain configured, add define( 'WPHOST_SMTP_AUTH', false ); to ignore them. Without an explicit override, one missing credential stops the send with a configuration error. Two empty credentials mean no authentication.

If your provider requires port 465, add define( 'WPHOST_SMTP_PORT', 465 ); to the host and credentials above. Port 465 implies implicit TLS, which encrypts the connection from the start. Certificate verification remains enabled.

Optional settings

These constants override the defaults. Use PHP booleans true and false without quotes.

ConstantBehaviour
WPHOST_SMTP_ENABLEDSet to false to leave WordPress's mail transport and sender untouched, so another mail plugin can take control.
WPHOST_SMTP_PORTDefaults to 465 with SECURE='ssl', otherwise 587 with authentication or SECURE='tls', otherwise 25.
WPHOST_SMTP_SECURE'tls' requires STARTTLS; 'ssl' uses implicit TLS; '' disables TLS, including automatic upgrades. If omitted, port 465 implies implicit TLS; other authenticated connections and port 587 require STARTTLS. Other unauthenticated ports use opportunistic STARTTLS.
WPHOST_SMTP_AUTHDefaults to true when either credential is nonempty. Authentication requires both credentials. Set to false only for a relay that accepts mail without a login.
WPHOST_SMTP_FROM_EMAILOverrides the From and bounce address independently of the login. Otherwise, an authenticated email-shaped username supplies the address, or the site's sender is preserved.
WPHOST_SMTP_FROM_NAMEOverrides the display name. Otherwise, the name supplied for that message is preserved. WPHOST_SMTP_USER_DISPLAYNAME remains an alias.

Setting a recognised WPHOST_SMTP_* constant without WPHOST_SMTP_HOST fails the send through wp_mail_failed, unless WPHOST_SMTP_ENABLED is false. Other malformed settings also fail the send. SMTP errors do not fall back to local mail. Disable TLS only for a trusted connection that requires it.

Use these settings with local Postfix

To choose a From and bounce address while keeping Postfix's queue, use the same configuration file and placement above. Replace any existing SMTP settings with:

define( 'WPHOST_SMTP_HOST', '127.0.0.1' ); define( 'WPHOST_SMTP_PORT', 25 ); define( 'WPHOST_SMTP_AUTH', false ); define( 'WPHOST_SMTP_SECURE', '' ); define( 'WPHOST_SMTP_FROM_EMAIL', 'bookings@example.com' ); define( 'WPHOST_SMTP_FROM_NAME', 'Example Website' );

Replace the sender with an address you control and monitor for delivery failures. The display name is optional. No password is needed. If WPHOST_SMTP_ENABLED is false, remove that setting to enable this configuration. The connection stays inside the VM; disabling TLS here does not change Postfix's onward delivery.

FROM_EMAIL sets both From and the bounce address. A separate bounce address, such as bounces@bounce.example.com, needs a mail plugin that supports a separate envelope sender, or a Postfix change scoped to envelope senders. Set up bounce reception with your existing mail provider. Adding a Return-Path header yourself does not change the SMTP envelope sender.

Example: SendGrid and other SMTP providers

SendGrid uses the literal username apikey. Since that login is not an email address, set WPHOST_SMTP_FROM_EMAIL to a sender verified with your provider:

define( 'WPHOST_SMTP_HOST', 'smtp.sendgrid.net' ); define( 'WPHOST_SMTP_USER', 'apikey' ); define( 'WPHOST_SMTP_PASS', 'your-sendgrid-api-key' ); define( 'WPHOST_SMTP_FROM_EMAIL', 'bookings@example.com' );

Amazon SES, Mailgun, Postmark, Brevo and SMTP2GO use the same configuration pattern. Use your provider's SMTP endpoint and SMTP credentials, which can differ from its API credentials. Amazon SES endpoints are regional.

Verify your sender address or domain with your provider and publish its required DNS records. SPF authorises sending services. DKIM signs messages. DMARC checks that SPF or DKIM aligns with the From domain and sets a policy for failures. Configure these for the actual sending path and keep existing authorisations for your other mail services. Keep your incoming-mail MX records unchanged for this SMTP setup.

Other mail plugins

WP Mail SMTP and FluentSMTP provide controls in wp-admin, including options to force the From email and name supplied by other plugins. Where supported, Return Path can match From. Providers may enforce their own sender or bounce address. Configure one mail plugin to manage delivery.

These settings support SMTP password authentication and relays without a login. OAuth and provider HTTP APIs require a separate mail plugin. Let that plugin handle mail by adding:

define( 'WPHOST_SMTP_ENABLED', false );

Use the PHP boolean false, not the string 'false'. Removing authentication does not grant permission to relay to external recipients.

Example: Microsoft 365 and Azure email

Use an existing Microsoft 365 mailbox: Choose a mail plugin's Microsoft OAuth connection, such as FluentSMTP's Outlook / Office 365 connection. Follow its Entra app registration and Microsoft sign-in steps; your tenant administrator may need to approve access. Use the authorised mailbox as From and set WPHOST_SMTP_ENABLED to false, as shown above. OAuth supports Microsoft sign-in with MFA. Password submission through smtp.office365.com:587 is a legacy option subject to tenant restrictions and Microsoft's Basic authentication deprecation.

Use Microsoft's SMTP service for application mail: Azure Communication Services Email works with the built-in settings. This is a separate paid Azure service. Connect an Email resource and verified domain, create an SMTP username linked to an Entra application, and use that application's client secret:

define( 'WPHOST_SMTP_HOST', 'smtp.azurecomm.net' ); define( 'WPHOST_SMTP_USER', 'your-azure-smtp-username' ); define( 'WPHOST_SMTP_PASS', 'your-entra-app-client-secret' ); define( 'WPHOST_SMTP_FROM_EMAIL', 'bookings@example.com' );

This uses port 587 and required STARTTLS. Use a sender at the verified domain and complete Azure's SPF and DKIM setup.

Send only to your Microsoft 365 organisation: If your tenant permits Direct Send, its MX endpoint can accept mail without a login. This endpoint does not accept SMTP username/password logins. Ask your administrator for the actual endpoint and an accepted sender domain. For example:

define( 'WPHOST_SMTP_HOST', 'example-com.mail.protection.outlook.com' ); define( 'WPHOST_SMTP_PORT', 25 ); define( 'WPHOST_SMTP_AUTH', false ); define( 'WPHOST_SMTP_SECURE', 'tls' ); define( 'WPHOST_SMTP_FROM_EMAIL', 'bookings@example.com' );

AUTH=false ignores old username and password settings; no custom mu-plugin is needed to disable login. Direct Send cannot deliver to external recipients. Authorise the VM in the sender domain's SPF as described under local delivery. Keep incoming-mail MX records unchanged. Microsoft's mail delivery guidance excludes third-party hosted applications from connector SMTP relay, so use OAuth or Azure Email for external recipients from a Woop! VM.

Example: Gmail

Gmail is practical for low-volume site mail. Google prefers OAuth, which this built-in configuration does not support. To use these constants, turn on 2-Step Verification and create a separate Google app password. Do not put your normal Google Account password in wp-config.php.

define( 'WPHOST_SMTP_HOST', 'smtp.gmail.com' ); define( 'WPHOST_SMTP_USER', 'your-account@gmail.com' ); define( 'WPHOST_SMTP_PASS', 'your-16-digit-app-password' );

Use your complete Gmail or Google Workspace email address. The authenticated address becomes both the From address and bounce address. Some managed Workspace, security-key-only and Advanced Protection accounts do not offer app passwords.

Software installed with your site

A small visible plugin plus must-use components connect WordPress to the managed platform.

ComponentWhat it doesYour control
Woop! Host default settings Suppresses routine successful-update email and provides SMTP configuration. Visible in Plugins. You can deactivate it.
Filesystem access notice Replaces an unusable FTP path with guidance and the correct panel link. A must-use plugin, readable in wp-content/mu-plugins.
Mail tracing Records delivery metadata and the originating code path, never the message body. A must-use plugin with tracing controls in wp-config.php.
Cache integrations Coordinates WordPress object and page caches, including cache purges after changes. Automatic. Content changes purge affected cached pages.

Must-use plugins load automatically and do not appear in the normal Plugins list. They do not collect visitor data or send site data elsewhere. You can inspect their files over SSH or SFTP.

Woop! Host

We handle the platform.

The virtual machine, PHP, database, web server, caches, certificates, backups, monitoring and system updates.

You

You shape the website.

Your content, themes, plugins, users and the choices you make in wp-admin.

The hosting panel exposes platform status and recent logs. The Resident Engineer can investigate the WordPress side in plain English.

Part three

When something works differently

Each answer stands alone because these links often reach customers in the middle of a problem.

“This site needs web code write access first”

You tried to install or update code in wp-admin while the stronger read-only-code posture was active.

This site needs web code write access first

New sites begin with web code write access while you build them. WordPress can install themes and plugins normally. If you later choose the stronger posture, PHP can run your code but cannot rewrite plugin or theme files.

This blocks a common persistence path: vulnerable plugin code can be exploited, but the PHP process cannot leave a new executable backdoor in the code directories. The trade-off is that browser installs need a temporary grant.

A

Grant temporary access in your hosting panel. Return to wp-admin and install normally.

B

Install with WP-CLI. It runs as the file owner and needs no grant.

C

Upload over SFTP. Then activate the theme or plugin in wp-admin.

D

Ask the Resident Engineer. Describe what you want installed and ask it to verify the result.

Web code write access

Writable during setup, optionally read-only later, and temporarily writable whenever you need it.

During normal setup, PHP-FPM can write the WordPress installer directories and update wp-config.php when WordPress or a plugin needs to change configuration. Your SSH and SFTP account can always write the files it owns.

After you choose the stronger read-only posture, grant bounded access from your hosting panel. An hour is usually plenty.

Temporary web code write access is granted until (date and time).

You can install themes and plugins normally until then, and plugins may update wp-config.php when needed. It switches off by itself — you do not need to do anything. While it is on, your site is missing one of its protections, and any file that appears in your themes or plugins folders is recorded, so it is worth finishing and not leaving it open.

Temporarily writable
  • Themes and plugins
  • Download and rollback work folders
  • wp-config.php for PHP-FPM
Still protected
  • WordPress core
  • wp-content itself and drop-ins
  • wp-config.php from direct NGINX access

You cannot grant access from SSH because changing the runtime's permissions is a privileged platform action. You do not need the grant for command-line installs:

wp plugin install woocommerce --activate wp theme install some-theme.zip --activate wp core update

A theme or plugin “does not exist”

If the files are visible over SSH or SFTP, the problem is usually their group rather than missing content.

PHP and NGINX read your files through the shared wp-site group. Some uploads, archive tools and rsync commands replace that group. WordPress can no longer read the component even though the files remain.

The hourly repair normally corrects this. To repair it immediately, run the copy installed inside your site shell:

/usr/local/bin/wphost-fix-webroot-perms

The shorter wphost-fix-webroot-perms form works too because the directory is on your command path. The command is safe to run at any time.

Who owns and serves your files

Four accounts divide ownership, PHP execution, static-file serving and platform administration.

AccountRoleFile access
wpuser You: SSH, SFTP, WP-CLI and the Resident Engineer. Owns the site and can change its files.
wp-php Runs WordPress PHP on each request. Runs code and writes runtime data. It can write installer paths while web code write access is granted.
www-data NGINX serving images, CSS and JavaScript. Reads public assets. Explicitly cannot read wp-config.php.
root Woop! Host platform administration. Not involved in serving your site.

Your files are owned by wpuser and grouped as wp-site. The group gives PHP and NGINX only the access required to serve the site:

-rw-r----- 1 wpuser wp-site 4096 Jul 29 10:12 index.php

Runtime-data directories under wp-content add group write access. Media, caches, translations and plugin data continue to work while executable code can remain read-only to PHP.

Keep this nearby

Quick reference

I want to…Do this
Install from wp-adminInstall normally during setup. Under the stronger posture, grant temporary web code write access.
Install without the panelSSH in and run wp plugin install <file>.zip --activate.
Upload filesUse SFTP over port 22.
Fix a missing theme or pluginRun wphost-fix-webroot-perms.
Inspect the databaseUse Adminer, mysql, or an SSH tunnel.
Keep a new plugin currentEnable its auto-update switch in wp-admin.
Take a backup nowRun wphost-backups.sh backup --reason "before my change".
Restore the siteChoose a run with wphost-backups.sh list, then preview restore --run RUN-ID --live --exact --dry-run.
Use my own SMTP providerAdd WPHOST_SMTP_HOST, WPHOST_SMTP_USER and WPHOST_SMTP_PASS to wp-config.php.
Move an existing site hereUse the wphost-migrate-export and wphost-migrate-import skills.
Work with a WordPress specialistSSH in, run claude or codex, and describe the outcome in plain English.

You do not have to manage the stack

You only need to know where your site ends
and where our work begins.

Your hosting panel shows the platform. The Resident Engineer knows WordPress, plugins, backups and this stack.

Go to your hosting panel