Skip to main content

Installing ACF and Preparing Environments

Reliable ACF work starts with deterministic installation and environment parity checks, not manual plugin clicks.

Learning Focus

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.

Core Idea

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

ApproachWhat HappensImpact in Production
Version-locked ACF Pro install with CLI verificationSame plugin behavior in local/staging/prodFewer release regressions and faster debugging
Manual install on each server without parity checksHidden version drift accumulatesInconsistent field behavior and intermittent template bugs
Runtime guard for ACF APIs (function_exists)Missing or broken installation is detected immediatelyPrevents silent failures in migrations and templates
Multisite activation plus per-site checksNetwork consistency is validated explicitlyReduces site-specific incidents after network deploys
Assume plugin is active because admin UI looked fine (wrong pattern)Automation runs against incomplete runtimeFailed migrations, missing field registration, and emergency hotfixes

Reference Table

Term/APISignature/SyntaxPurposeKey Notes
Plugin activationwp plugin activate advanced-custom-fields-proEnable ACF Pro in target environmentUse --network for multisite network-wide activation
Plugin version querywp plugin get advanced-custom-fields-pro --field=versionConfirm installed versionCompare output across local/staging/prod
API availability checkfunction_exists('acf_get_field_groups'): boolValidate runtime API presenceRun via wp eval in deploy gates
acf_get_field_groups()acf_get_field_groups(array $filter = []): arrayVerify runtime schema registry is aliveNon-zero count usually expected on configured systems
acf/initadd_action('acf/init', callable $callback): voidSafe hook for ACF-dependent boot logicUse guards in callbacks to log diagnostics
update_option()update_option(string $option, mixed $value, bool $autoload = null): boolPersist environment fingerprint reportUseful for audit trails and CLI inspection
Repeater/Flexible supporthave_rows(), get_row_layout()Pro-only field runtime behaviorACF Pro Required when those field types are used
wp eval-filewp eval-file <path>Execute environment verification scriptsGood for CI and one-command smoke checks
ACF Pro Required

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.

  1. Activate ACF Pro via WP-CLI.
  2. Add a runtime guard file in the theme bootstrap path.
  3. Register checks on after_setup_theme and acf/init.
  4. Store a structured installation report in an option.
  5. Inspect the report and function availability from CLI.
wp-content/themes/clinic-pro/inc/acf/bootstrap-guard.php
<?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);
});
terminal: command
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;'
terminal: output
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
warning

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.

  1. Add a parity reporter hooked to acf/init.
  2. Compute a fingerprint from version + group count + key feature checks.
  3. Persist the fingerprint in options for later comparison.
  4. Query the fingerprint in local, staging, and production.
  5. Block releases when fingerprints differ unexpectedly.
wp-content/themes/clinic-pro/inc/acf/environment-fingerprint.php
<?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));
});
terminal: command
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
terminal: output
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
note

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

scripts/acf-multisite-fragile-check.php
<?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

scripts/acf-multisite-robust-check.php
<?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;
}
terminal: command
wp eval-file scripts/acf-multisite-fragile-check.php
wp eval-file scripts/acf-multisite-robust-check.php
terminal: output
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
terminal: command
wp eval 'switch_to_blog(2); var_export(function_exists("acf_get_field_groups")); restore_current_blog(); echo PHP_EOL;'
terminal: output
false
warning

In multisite, network activation is necessary but not sufficient. Always validate API availability and group count per site.

Common Mistakes

MistakeRoot CauseWhat Breaks in ProductionCorrect Pattern
Verifying only activation statusTeam skips runtime API checksCLI scripts and template hooks fail despite active pluginwp eval 'var_export(function_exists("acf_get_field_groups"));'
Different ACF Pro versions across environmentsNo version lock in release processInconsistent field behavior and migration errorswp plugin get advanced-custom-fields-pro --field=version in every environment
Running migrations before installation checksWrong script order in CI/CDupdate_field() operations fail or write invalid dataGate first: wp eval 'if(!function_exists("acf_get_field_groups")){exit(1);} echo "ok";'
Ignoring multisite per-site validationAssumes network activation guarantees parityOne or more sites fail at runtimewp eval-file scripts/acf-multisite-robust-check.php
Not persisting install diagnosticsNo historical record for incidentsSlower root-cause analysis during outagesupdate_option('acf_install_guard_report', [...]); and inspect with CLI
Depending on admin UI screenshotsProcess is non-repeatable and not automatableSetup drift during handoffs and server rebuildsUse 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

  1. 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;'
  2. Record installation diagnostics in options: update_option('acf_install_guard_report', [...], false);
  3. Validate plugin version parity before schema sync: wp plugin get advanced-custom-fields-pro --field=version
  4. Run per-site checks on multisite: wp eval-file scripts/acf-multisite-robust-check.php
  5. Check runtime field-group counts after activation: wp eval 'echo count(acf_get_field_groups()) . PHP_EOL;'
  6. Keep installation commands in repository scripts, not tribal memory: store in scripts/acf-install-verify.sh.
  7. 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

CommandPurposeReal Example Output
wp plugin activate advanced-custom-fields-proActivate ACF ProSuccess: Plugin 'advanced-custom-fields-pro' activated.
wp plugin get advanced-custom-fields-pro --fields=name,status,versionRead plugin activation/version statusadvanced-custom-fields-pro active 6.3.1
wp eval 'var_export(function_exists("acf_get_field_groups"));'Confirm API availabilitytrue
wp eval 'echo count(acf_get_field_groups()) . PHP_EOL;'Count runtime groups12
wp eval 'do_action("after_setup_theme"); print_r(get_option("acf_install_guard_report"));'Inspect install guard reportArray ( [plugin_active] => 1 ... )
wp eval 'do_action("acf/init"); print_r(get_option("acf_environment_fingerprint"));'Inspect parity fingerprintArray ( [payload] => Array ... )
wp eval-file scripts/acf-multisite-robust-check.phpValidate per-site readiness in multisitesite=1 active=1 api=1 groups=12
wp post meta get 1204 service_headlineVerify data write path after installation baselineInstall Baseline Ready

What's Next

tip

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.