WordPress VIP Go is Automattic’s enterprise hosting platform, and the first thing that catches people out is that a VIP repo does not look like a WordPress install. This covers the directory structure the platform expects, the four environments, getting a local one running with the VIP CLI, and the coding rules VIP actually enforces.
Eight required directories
- client-mu-plugins, plugins, themes
- vip-config, private, images
- docs, languages
- All eight required, none may be removed
Any directory you add
- Not mounted by the platform
- Not web accessible
- The code in it never runs
docs/ is required as well, but it is the one directory not mounted to production.
What VIP Go is
Enterprise hosting at wpvip.com, built so that a site does not go down for performance reasons.
What you get in the repo is not a WordPress codebase. It is a wrapper around one. Every application lives in a private repo under the @wpcomvip GitHub organisation, based on the same starting point:
https://github.com/Automattic/vip-go-skeletonAdmins hold repo access and everything ships through git. There is no plugin installer and no theme uploader. ๐
The directory structure
client-mu-plugins/-> always-active global plugins, becausemu-pluginsis not yoursdocs/-> documentation, and the one directory not mounted to productionimages/-> faviconslanguages/->.poand.motranslation files, produced by making the plugin translatable in the first placeplugins/-> regular pluginsprivate/-> readable by your code, not by the publicthemes/-> your themesvip-config/->vip-config.phpandsunrise.php
All eight are required and must not be removed. The rule that catches people is the other direction: any directory you add that is not on that list does not get mounted, so it is not web accessible and the code in it never runs.
client-mu-plugins/ loads a single-file plugin automatically. A plugin in a subdirectory does not load at all until you require it from client-mu-plugins/plugin-loader.php.
images/ is for favicons specifically. Other public images belong in the media library or in your theme’s assets, not here.
private/ is not publicly reachable but your own code can read it:
file_get_contents( WPCOM_VIP_PRIVATE_DIR . '/sites.json' );Treat it as read-only. The whole repo is deployed from git, so anything written at runtime is not going to survive the next deploy.
vip-config/vip-config.php is where your wp-config.php constants go. One warning that is easy to learn the hard way: it loads immediately after wp-config.php, so most WordPress functions, classes and APIs do not exist yet. Pure PHP only. ๐
The four environments
- Production -> public
- Preprod -> production-shaped, where testing happens before a release
- Develop -> non-production, for debugging
- Local -> your machine
Each environment tracks a specific branch of the GitHub repo, so deploying is pushing.
Every application gets exactly one production environment. Non-production environments are created in the VIP Dashboard, and how many you are allowed is set by the contract rather than by you. So the number of places you can safely break something is a commercial question. ๐คก
Installing the VIP CLI
The CLI is node based:
npm install -g @automattic/vipThen create an environment and start it:
vip dev-env create
vip dev-env startcreate walks through the options. start prints the paths and URLs to reach the environment.
Keep Node on an LTS release. Locally built assets get committed to the repo as static files, so the version you build on is the version the next person inherits. ๐ฆ
vip-config.php runs immediately after wp-config.php, before most WordPress functions, classes and APIs exist.
The rules VIP enforces
Commits:
- one feature or one change per commit, related changes bundled together
- atomic -> no commit leaves the tree in a broken state
- present tense, semantic, self descriptive
Queries:
- use WordPress API functions instead of going at the database directly
$wpdb->prepareon anything that takes input- keep the SQL simple and do the logic in PHP, which is what scales
- no
DISTINCT, noGROUP BY, nothing that builds a temporary table - cache the results where caching makes sense
That second list is not a style preference. It is the platform’s performance budget, written down.
Over-fetch, then filter in PHP
The pattern VIP pushes you toward: ask for a few more rows than you need, then discard in PHP rather than making the database do the thinking.
$exclude = array( get_the_ID(), ...$suggested_posts );
$args = array(
'posts_per_page' => 7,
'author' => get_the_author_meta( 'ID' ),
'post_type' => 'post',
'post_status' => 'publish',
'post__not_in' => get_option( 'sticky_posts' ),
'orderby' => 'date',
'order' => 'DESC',
);
$result = new WP_Query( $args );Seven posts to end up with three: three you want, the current one, and three already suggested on the page. Then loop, skip anything in $exclude, check the post date in PHP, and break once three have printed. ๐ฃ
Environment-specific code hangs off a constant the platform sets for you, VIP_GO_APP_ENVIRONMENT. The skeleton’s own vip-config.php uses it exactly this way:
if ( ( ! defined( 'VIP_GO_APP_ENVIRONMENT' ) || ( defined( 'VIP_GO_APP_ENVIRONMENT' ) && 'production' !== VIP_GO_APP_ENVIRONMENT ) )
&& ! defined( 'WP_DEBUG' ) ) {
define( 'WP_DEBUG', true );
}Note the shape of that check. It treats “constant not defined” as non-production, because local environments do not define it.
PHPCS comes with the repo
The skeleton ships a .phpcs.xml.dist and a composer.json that installs three standards for you: VIP Coding Standards, WordPress Coding Standards, and PHPCompatibilityWP. ๐
composer install
vendor/bin/phpcsRun it from whichever directory holds the relevant .phpcs.xml.dist, because themes and custom plugins can extend the root config with their own. If you have not set PHPCS up outside VIP before, the local setup guide covers the standalone install and phpcbf.
What this does not cover
Deploys themselves, the support ticket process for extra environments, and VIP’s own caching layer.
This started as notes taken while learning the platform in 2022 and every claim above was re-checked against the current Automattic/vip-go-skeleton repository and VIP docs before publishing. Two things had already changed in that time: the old /config directory with its .vip.[app].[environment].yml domain mapping files is gone, and extra environments come from the VIP Dashboard now rather than a support ticket. ๐ฐ๏ธ
