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.
About this guideThis 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.
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.
VisitorBrowser
HTTPS request
→
Secure edgeNGINX + TLS
Encryption and static files
→
Page cacheVarnish
Fast anonymous visits
→
ApplicationPHP-FPM
Runs WordPress + microcache
→
SiteWordPress
Themes, plugins and content
→
DataMariaDB + 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.
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:
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
Move the files
Use SFTP, rsync or SSH from the old host.
2
Import the database
Use Adminer, mysql or WP-CLI.
3
Update URLs safely
Run wp search-replace with --dry-run first.
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.
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
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.
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.
01
Back up files and database
A restore point exists before anything changes.
02
Update WordPress core
Core is brought up to the current release.
03
Update opted-in plugins and themes
Only items with WordPress auto-update enabled are included.
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.
The 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-To
Where 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-Path
Where 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 username
The 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.
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:
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.
Constant
Behaviour
WPHOST_SMTP_ENABLED
Set to false to leave WordPress's mail transport and sender untouched, so another mail plugin can take control.
WPHOST_SMTP_PORT
Defaults 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_AUTH
Defaults 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_EMAIL
Overrides 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_NAME
Overrides 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:
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:
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:
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:
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.
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.
Component
What it does
Your 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.
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:
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.
Account
Role
File 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:
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.