Installing ACF and Preparing Environments
Reliable ACF work starts with deterministic installation and environment parity checks, not manual plugin clicks.
You will build a repeatable installation workflow for ACF Pro using code and CLI, enforce environment parity, and implement runtime guards that fail fast when ACF is missing or incompatible. This is essential because every later schema and template lesson assumes the installation baseline is correct.
Concept Overview
Many teams treat ACF installation as a one-time setup task. In real delivery environments, it is an ongoing operational concern: new servers spin up, staging resets happen, and plugins are upgraded during release cycles. If ACF versions diverge or activation state differs, schema behavior diverges too.
A production-safe approach uses three layers: installation (plugin present and activated), runtime validation (ACF API functions available), and parity checks (same version and baseline counts across all environments). WP-CLI is ideal for this because it gives deterministic, scriptable checks that can run in CI, deploy hooks, and incident playbooks.
For ACF Pro specifically, version and licensing workflows matter because Pro-only field types (Repeater, Flexible Content, Clone, Gallery, ACF Blocks, Options Pages) are often business-critical. Even when license handling is externalized, your project still needs explicit runtime guards so missing APIs fail loudly in logs and deployment checks.
Treat ACF Pro installation as an auditable deployment dependency: install and activate with CLI, assert API availability, and verify environment parity before touching schema or data.
Important variants in real projects:
- Direct ZIP/manual deployment + WP-CLI activation: useful on legacy hosts with no Composer.
- Composer-managed version lock: preferred for deterministic builds and CI/CD.
- Multisite network activation: one command, but still requires per-site API checks.
- Guarded boot pattern: custom code logs and blocks dependent workflows if ACF APIs are unavailable.
Why It Matters
| Approach | What Happens | Impact in Production |
|---|---|---|
| Version-locked ACF Pro install with CLI verification | Same plugin behavior in local/staging/prod | Fewer release regressions and faster debugging |
| Manual install on each server without parity checks | Hidden version drift accumulates | Inconsistent field behavior and intermittent template bugs |
Runtime guard for ACF APIs (function_exists) | Missing or broken installation is detected immediately | Prevents silent failures in migrations and templates |
| Multisite activation plus per-site checks | Network consistency is validated explicitly | Reduces site-specific incidents after network deploys |
| Assume plugin is active because admin UI looked fine (wrong pattern) | Automation runs against incomplete runtime | Failed migrations, missing field registration, and emergency hotfixes |
Reference Table
| Term/API | Signature/Syntax | Purpose | Key Notes |
|---|---|---|---|
| Plugin activation | wp plugin activate advanced-custom-fields-pro | Enable ACF Pro in target environment | Use --network for multisite network-wide activation |
| Plugin version query | wp plugin get advanced-custom-fields-pro --field=version | Confirm installed version | Compare output across local/staging/prod |
| API availability check | function_exists('acf_get_field_groups'): bool | Validate runtime API presence | Run via wp eval in deploy gates |
acf_get_field_groups() | acf_get_field_groups(array $filter = []): array | Verify runtime schema registry is alive | Non-zero count usually expected on configured systems |
acf/init | add_action('acf/init', callable $callback): void | Safe hook for ACF-dependent boot logic | Use guards in callbacks to log diagnostics |
update_option() | update_option(string $option, mixed $value, bool $autoload = null): bool | Persist environment fingerprint report | Useful for audit trails and CLI inspection |
| Repeater/Flexible support | have_rows(), get_row_layout() | Pro-only field runtime behavior | ACF Pro Required when those field types are used |
wp eval-file | wp eval-file <path> | Execute environment verification scripts | Good for CI and one-command smoke checks |
This lesson validates installation for Pro-only capabilities used later in the curriculum, including Repeater, Flexible Content, ACF Blocks, and Options pages.
Practical Use Cases
Use Case 1 — Install ACF Pro and enforce runtime guardrails on every boot
A team deploys the same WordPress codebase to local, staging, and production. They need an install-and-verify path that can be executed without admin UI and a guard file that logs whether ACF APIs are available.
- Activate ACF Pro via WP-CLI.
- Add a runtime guard file in the theme bootstrap path.
- Register checks on
after_setup_themeandacf/init. - Store a structured installation report in an option.
- Inspect the report and function availability from CLI.
<?php
declare(strict_types=1);
add_action('after_setup_theme', function (): void {
$pluginActive = is_plugin_active('advanced-custom-fields-pro/acf.php');
$apiReady = function_exists('acf_get_field_groups');
$version = 'unknown';
if ($pluginActive && function_exists('get_plugin_data')) {
$pluginFile = WP_PLUGIN_DIR . '/advanced-custom-fields-pro/acf.php';
if (file_exists($pluginFile)) {
$data = get_plugin_data($pluginFile, false, false);
$version = (string) ($data['Version'] ?? 'unknown');
}
}
update_option('acf_install_guard_report', [
'plugin_active' => $pluginActive,
'api_ready' => $apiReady,
'version' => $version,
'checked_at' => gmdate('c'),
], false);
if (!$pluginActive || !$apiReady) {
error_log('[acf-install-guard] ACF Pro missing or API unavailable');
}
});
add_action('acf/init', function (): void {
$groupCount = count(acf_get_field_groups());
update_option('acf_install_runtime_group_count', $groupCount, false);
error_log('[acf-install-guard] runtime_groups=' . $groupCount);
});
wp plugin activate advanced-custom-fields-pro
wp eval 'do_action("after_setup_theme"); print_r(get_option("acf_install_guard_report"));'
wp eval 'do_action("acf/init"); echo get_option("acf_install_runtime_group_count") . PHP_EOL;'
Success: Plugin 'advanced-custom-fields-pro' activated.
Array
(
[plugin_active] => 1
[api_ready] => 1
[version] => 6.3.1
[checked_at] => 2026-02-23T11:25:09+00:00
)
12
Do not assume "plugin active" implies "runtime ready". Function availability and hook execution still need explicit verification.
Use Case 2 — Build an environment parity fingerprint for release gates
A release manager wants one command that answers: "Is this environment ready for ACF schema deployment?" The command should report plugin version, API availability, group count, and critical Pro capability checks.
- Add a parity reporter hooked to
acf/init. - Compute a fingerprint from version + group count + key feature checks.
- Persist the fingerprint in options for later comparison.
- Query the fingerprint in local, staging, and production.
- Block releases when fingerprints differ unexpectedly.
<?php
declare(strict_types=1);
add_action('acf/init', function (): void {
$pluginVersion = trim((string) shell_exec('wp plugin get advanced-custom-fields-pro --field=version 2>/dev/null'));
if ($pluginVersion === '') {
$pluginVersion = 'unknown';
}
$groups = acf_get_field_groups();
$groupCount = count($groups);
$featureChecks = [
'has_repeater_api' => function_exists('have_rows'),
'has_layout_api' => function_exists('get_row_layout'),
'has_options_api' => function_exists('acf_add_options_page'),
];
$fingerprintPayload = [
'acf_version' => $pluginVersion,
'group_count' => $groupCount,
'features' => $featureChecks,
];
$fingerprint = hash('sha256', wp_json_encode($fingerprintPayload));
update_option('acf_environment_fingerprint', [
'payload' => $fingerprintPayload,
'fingerprint' => $fingerprint,
'generated_at' => gmdate('c'),
], false);
});
add_action('admin_init', function (): void {
$report = get_option('acf_environment_fingerprint');
if (!is_array($report)) {
error_log('[acf-fingerprint] missing report');
return;
}
error_log('[acf-fingerprint] ' . wp_json_encode($report));
});
wp eval 'do_action("acf/init"); print_r(get_option("acf_environment_fingerprint"));'
wp eval 'echo hash("sha256", wp_json_encode(get_option("acf_environment_fingerprint")["payload"])) . PHP_EOL;'
wp plugin get advanced-custom-fields-pro --fields=name,status,version
Array
(
[payload] => Array
(
[acf_version] => 6.3.1
[group_count] => 12
[features] => Array
(
[has_repeater_api] => 1
[has_layout_api] => 1
[has_options_api] => 1
)
)
[fingerprint] => 4f5b9a6f75a8f31a2d4e6f6772199c57811c93a53dbf8c2f31f48df2c6a5f5ae
[generated_at] => 2026-02-23T11:29:54+00:00
)
4f5b9a6f75a8f31a2d4e6f6772199c57811c93a53dbf8c2f31f48df2c6a5f5ae
name status version
advanced-custom-fields-pro active 6.3.1
A fingerprint does not replace tests, but it quickly catches environment drift before schema migrations begin.
Use Case 3 — Edge case: activation succeeds but multisite parity still fails
On multisite, a team runs network activation and assumes every site is ready. In reality, one site has an outdated must-use plugin that blocks ACF bootstrap hooks, causing partial runtime failure.
❌ Fragile Pattern
<?php
declare(strict_types=1);
if (!is_multisite()) {
echo "not_multisite" . PHP_EOL;
return;
}
wp_plugin_activate('advanced-custom-fields-pro/acf.php', '', true);
echo "network_activation_done" . PHP_EOL;
echo "ready" . PHP_EOL;
✅ Robust Pattern
<?php
declare(strict_types=1);
if (!is_multisite()) {
echo "not_multisite" . PHP_EOL;
return;
}
$sites = get_sites(['number' => 0]);
$report = [];
foreach ($sites as $site) {
switch_to_blog((int) $site->blog_id);
$pluginActive = is_plugin_active('advanced-custom-fields-pro/acf.php');
$apiReady = function_exists('acf_get_field_groups');
$groupCount = $apiReady ? count(acf_get_field_groups()) : 0;
$report[] = [
'site_id' => get_current_blog_id(),
'plugin_active' => $pluginActive,
'api_ready' => $apiReady,
'group_count' => $groupCount,
];
restore_current_blog();
}
foreach ($report as $row) {
echo 'site=' . $row['site_id']
. ' active=' . (int) $row['plugin_active']
. ' api=' . (int) $row['api_ready']
. ' groups=' . $row['group_count']
. PHP_EOL;
}
wp eval-file scripts/acf-multisite-fragile-check.php
wp eval-file scripts/acf-multisite-robust-check.php
network_activation_done
ready
site=1 active=1 api=1 groups=12
site=2 active=1 api=0 groups=0
site=3 active=1 api=1 groups=12
wp eval 'switch_to_blog(2); var_export(function_exists("acf_get_field_groups")); restore_current_blog(); echo PHP_EOL;'
false
In multisite, network activation is necessary but not sufficient. Always validate API availability and group count per site.
Common Mistakes
| Mistake | Root Cause | What Breaks in Production | Correct Pattern |
|---|---|---|---|
| Verifying only activation status | Team skips runtime API checks | CLI scripts and template hooks fail despite active plugin | wp eval 'var_export(function_exists("acf_get_field_groups"));' |
| Different ACF Pro versions across environments | No version lock in release process | Inconsistent field behavior and migration errors | wp plugin get advanced-custom-fields-pro --field=version in every environment |
| Running migrations before installation checks | Wrong script order in CI/CD | update_field() operations fail or write invalid data | Gate first: wp eval 'if(!function_exists("acf_get_field_groups")){exit(1);} echo "ok";' |
| Ignoring multisite per-site validation | Assumes network activation guarantees parity | One or more sites fail at runtime | wp eval-file scripts/acf-multisite-robust-check.php |
| Not persisting install diagnostics | No historical record for incidents | Slower root-cause analysis during outages | update_option('acf_install_guard_report', [...]); and inspect with CLI |
| Depending on admin UI screenshots | Process is non-repeatable and not automatable | Setup drift during handoffs and server rebuilds | Use committed guard files + WP-CLI output as source of truth |
Deep Dive: Why "Plugin Active" Is Not Enough
Activation status checks one layer only: whether WordPress marks the plugin as enabled. It does not prove ACF APIs are callable in the current execution context, nor that hooks run in expected order. In multisite, this gap becomes dangerous because one site can be functionally broken while network-level status still reports success. Teams often discover this only after schema migration commands fail under load. A better pattern is to pair activation checks with function checks and runtime group counts.
wp eval 'echo "active=" . (int) is_plugin_active("advanced-custom-fields-pro/acf.php") . PHP_EOL; echo "api=" . (int) function_exists("acf_get_field_groups") . PHP_EOL; echo "groups=" . (function_exists("acf_get_field_groups") ? count(acf_get_field_groups()) : 0) . PHP_EOL;'
Best Practices
- Gate all ACF-dependent deploy tasks on API readiness:
wp eval 'if(!function_exists("acf_get_field_groups")){exit(1);} echo "acf-ready" . PHP_EOL;' - Record installation diagnostics in options:
update_option('acf_install_guard_report', [...], false); - Validate plugin version parity before schema sync:
wp plugin get advanced-custom-fields-pro --field=version - Run per-site checks on multisite:
wp eval-file scripts/acf-multisite-robust-check.php - Check runtime field-group counts after activation:
wp eval 'echo count(acf_get_field_groups()) . PHP_EOL;' - Keep installation commands in repository scripts, not tribal memory: store in
scripts/acf-install-verify.sh. - Use one baseline fingerprint per release train:
wp eval 'print_r(get_option("acf_environment_fingerprint"));'
Hands-On Practice
Exercise 1: Activate ACF Pro and verify API readiness
Run the installation checks:
wp plugin activate advanced-custom-fields-pro
wp eval 'var_export(function_exists("acf_get_field_groups")); echo PHP_EOL;'
wp eval 'echo count(acf_get_field_groups()) . PHP_EOL;'
After completing this exercise, expected output pattern:
Success: Plugin 'advanced-custom-fields-pro' activated.
true
12
Exercise 2: Add bootstrap guard and inspect persisted report
Create wp-content/themes/clinic-pro/inc/acf/bootstrap-guard.php using Use Case 1 code, then run:
wp eval 'do_action("after_setup_theme"); print_r(get_option("acf_install_guard_report"));'
After completing this exercise, output should include:
[plugin_active] => 1
[api_ready] => 1
[version] =>
Exercise 3: Generate parity fingerprint
Create wp-content/themes/clinic-pro/inc/acf/environment-fingerprint.php and run:
wp eval 'do_action("acf/init"); print_r(get_option("acf_environment_fingerprint"));'
After completing this exercise, output should include:
[acf_version] =>
[group_count] =>
[fingerprint] =>
Exercise 4: Create a test page for later schema lessons
Create a page and verify you can store a field value:
wp post create --post_title="ACF Install Baseline" --post_status=publish --post_type=page
wp eval '$id=(int)get_page_by_title("ACF Install Baseline", OBJECT, "page")->ID; update_field("service_headline","Install Baseline Ready",$id); echo get_field("service_headline",$id) . PHP_EOL;'
After completing this exercise, expected final output:
Install Baseline Ready
Exercise 5: Run a full release-gate checklist
Run these commands in sequence:
wp plugin get advanced-custom-fields-pro --fields=name,status,version
wp eval 'echo "api=" . (int) function_exists("acf_get_field_groups") . PHP_EOL; echo "groups=" . count(acf_get_field_groups()) . PHP_EOL;'
wp eval 'print_r(get_option("acf_environment_fingerprint"));'
After completing this exercise, expected output pattern:
advanced-custom-fields-pro active 6.3.1
api=1
groups=12
Array
(
[fingerprint] =>
)
CLI Reference
| Command | Purpose | Real Example Output |
|---|---|---|
wp plugin activate advanced-custom-fields-pro | Activate ACF Pro | Success: Plugin 'advanced-custom-fields-pro' activated. |
wp plugin get advanced-custom-fields-pro --fields=name,status,version | Read plugin activation/version status | advanced-custom-fields-pro active 6.3.1 |
wp eval 'var_export(function_exists("acf_get_field_groups"));' | Confirm API availability | true |
wp eval 'echo count(acf_get_field_groups()) . PHP_EOL;' | Count runtime groups | 12 |
wp eval 'do_action("after_setup_theme"); print_r(get_option("acf_install_guard_report"));' | Inspect install guard report | Array ( [plugin_active] => 1 ... ) |
wp eval 'do_action("acf/init"); print_r(get_option("acf_environment_fingerprint"));' | Inspect parity fingerprint | Array ( [payload] => Array ... ) |
wp eval-file scripts/acf-multisite-robust-check.php | Validate per-site readiness in multisite | site=1 active=1 api=1 groups=12 |
wp post meta get 1204 service_headline | Verify data write path after installation baseline | Install Baseline Ready |
What's Next
- Continue to Field Groups, Location Rules, and Naming Conventions.
- Return to Module 1 Overview for the full onboarding sequence.
- Related lesson: Local JSON and Version Control Workflow.
- Related lesson: Programmatic Registration and PHP Exports.
Revisit this lesson when creating a new environment (new VPS, staging clone, or disaster-recovery restore) and you need a fast, reliable ACF readiness checklist.