{"id":19252952,"url":"https://github.com/frc/bedrock-on-gae","last_synced_at":"2026-05-06T00:05:25.446Z","repository":{"id":140523052,"uuid":"142694199","full_name":"frc/bedrock-on-gae","owner":"frc","description":"How to deploy WP (Bedrock style) to Google App Engine (standard environment). Committed and documented step-by-step.","archived":false,"fork":false,"pushed_at":"2018-08-01T12:58:47.000Z","size":43,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-27T15:02:41.355Z","etag":null,"topics":["auth0","bedrock","google-app-engine","wordpress"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/frc.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-07-28T16:50:14.000Z","updated_at":"2021-12-28T06:23:45.000Z","dependencies_parsed_at":null,"dependency_job_id":"5ed09440-4461-4e49-af75-461a6955572f","html_url":"https://github.com/frc/bedrock-on-gae","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/frc/bedrock-on-gae","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frc%2Fbedrock-on-gae","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frc%2Fbedrock-on-gae/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frc%2Fbedrock-on-gae/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frc%2Fbedrock-on-gae/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/frc","download_url":"https://codeload.github.com/frc/bedrock-on-gae/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frc%2Fbedrock-on-gae/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32672685,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-05T11:29:49.557Z","status":"ssl_error","status_checked_at":"2026-05-05T11:29:48.587Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["auth0","bedrock","google-app-engine","wordpress"],"created_at":"2024-11-09T18:29:00.780Z","updated_at":"2026-05-06T00:05:25.428Z","avatar_url":"https://github.com/frc.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# WordPress on GAE\n\n## Bedrock initial setup\n\n  Create an empty working directory and initialize a new roots/bedrock project in it.\n\n    mkdir my-project\n    cd my-project\n    composer create-project roots/bedrock .\n\n  Add recommended `.editorconfig` setup.\n\n    # editorconfig.org\n\n    root = true\n\n    [*]\n    indent_style = space\n    indent_size = 2\n    end_of_line = lf\n    charset = utf-8\n    trim_trailing_whitespace = true\n    insert_final_newline = true\n\n    [*.php]\n    indent_size = 4\n\n## Install plugins \u0026 drop-ins\n\n  Define how to setup WP drop-ins.\n\n    composer require koodimonni/composer-dropin-installer\n\n  Add to `composer.json` key `extra`:\n\n    \"dropin-paths\": {\n      \"web/app\": [\n        \"package:wpackagist-plugin/memcached-redux:object-cache.php\",\n        \"package:wpackagist-plugin/batcache:advanced-cache.php\",\n        \"type:wordpress-dropin\"\n      ]\n    }\n  \n  Require the basic modules.\n\n    composer require frc/wp-harness \\\n      wpackagist-plugin/google-app-engine \\\n      wpackagist-plugin/batcache \\\n      wpackagist-plugin/memcached-redux \\\n      wpackagist-plugin/auth0\n\n    composer require --dev google/appengine-php-sdk\n\n  Ignore the two drop-ins in version control.\n\n      echo '/advanced-cache.php' \u003e\u003eweb/app/.gitignore\n      echo '/object-cache.php' \u003e\u003eweb/app/.gitignore\n\n## Local database and user setup\n\n  Create local MySQL database, eg.\n\n    mysql -uroot \u003c\u003cEOD\n    \u003e create database wp;\n    \u003e create user 'wp' identified by 'wp';\n    \u003e grant all on wp.* to 'wp';\n    \u003e EOD\n\n  Setup `.env` for local development:\n\n    DB_NAME=wp\n    DB_USER=wp\n    DB_PASSWORD=wp\n    DB_HOST=127.0.0.1\n    WP_ENV=development\n    WP_HOME=http://localhost:8080\n    WP_SITEURL=${WP_HOME}/wp\n\n  Initialize the database.\n\n    vendor/bin/wp core install \\\n      --url=localhost --title=wp \\\n      --admin_user=admin \\\n      --admin_email=first.last@example.com\n\n    Admin password: *****\n    Success: WordPress installed successfully.\n\n  Verify the installation with eg. `vendor/bin/wp db check`,\n  list the plugins with `vendor/bin/wp plugin list` to verify\n  all tooling and paths are in place. The output should look like this.\n\n    +--------------------------+----------+--------+---------+\n    | name                     | status   | update | version |\n    +--------------------------+----------+--------+---------+\n    | batcache                 | inactive | none   | 1.2     |\n    | google-app-engine        | inactive | none   | 1.6     |\n    | auth0                    | inactive | none   | 3.6.2   |\n    | bedrock-autoloader       | must-use | none   | 1.0.0   |\n    | disallow-indexing        | must-use | none   | 1.0.0   |\n    | register-theme-directory | must-use | none   | 1.0.0   |\n    | advanced-cache.php       | dropin   | none   |         |\n    | object-cache.php         | dropin   | none   |         |\n    +--------------------------+----------+--------+---------+\n\n  Next step is to setup actual HTTP (development) server and\n  continue plugin setup on the browser.\n\n## Prepare the GAE development setup\n\n  Install Google Cloud SDK. Download it from https://cloud.google.com/sdk/ and follow the setup instructions, or install it with `brew cask`.\n  \n    brew cask install google-cloud-sdk\n\n  Create `app.yaml` that defines the application routes etc.\n\n    runtime: php55\n    api_version: 1\n\n    handlers:\n    # Wordpress core as-is\n    - url: /wp/(.*\\.(htm|html|css|js|ico|jpg|jpeg|png|gif|woff|ttf|otf|eot|svg))\n      static_files: web/wp/\\1\n      upload: web/wp/.*\\.(htm|html|css|js|ico|jpg|jpeg|png|gif|woff|ttf|otf|eot|svg)$\n      application_readable: true\n\n    # User content: themes, plugins, uploads\n    - url: /app/(.*\\.(htm|html|css|js|ico|jpg|jpeg|png|gif|woff|ttf|otf|eot|svg))\n      static_files: web/app/\\1\n      upload: web/app/.*\\.(htm|html|css|js|ico|jpg|jpeg|png|gif|woff|ttf|otf|eot|svg)$\n      application_readable: true\n\n    # Entry URL for wp-admin\n    - url: /wp/wp-admin/\n      script: web/wp/wp-admin/index.php\n      secure: always\n\n    # All other wp-admin components\n    - url: /wp/wp-admin/(.+)\n      script: web/wp/wp-admin/\\1\n      secure: always\n\n    # Handle wp-login separately, requires https\n    - url: /wp/wp-login.php\n      script: web/wp/wp-login.php\n      secure: always\n\n    # Handle wp-cron separatelyu, only admin (see cron.yaml) can call\n    - url: /wp/wp-cron.php\n      script: web/wp/wp-cron.php\n      login: admin\n\n    # Handle all other WP core entrypoints\n    - url: /wp/wp-(.+).php\n      script: web/wp/wp-\\1.php\n\n    - url: /wp/xmlrpc.php\n      script: web/wp/xmlrpc.php\n\n    # Other URLs are handled by the main index.php (ie. bedrock),\n    # which then loads the config, WP core etc.\n    - url: /(.+)?/?\n      script: web/index.php\n\nCreate `cron.yaml` eg.\n\n    cron:\n    - description: \"wordpress cron tasks\"\n      url: /wp/wp-cron.php\n      schedule: every 15 minutes\n\nCreate `php.ini` eg.\n\n    google_app_engine.enable_functions = \"php_sapi_name, gc_enabled\"\n    allow_url_include = \"1\"\n    upload_max_filesize = \"8M\"\n    google_app_engine.disable_readonly_filesystem = \"1\"\n    ;google_app_engine.allow_include_gs_buckets = \"1\"\n\nStart the development server.\n\n    dev_appserver.py app.yaml\n\n## Auth0 setup\n\nGo to URL http://localhost:8080 to browse the site. Open the wp-admin\nat http://localhost:8080/wp/wp-admin/ to finalize the plugin setup.\nSign-in with the administrator user and password defined above.\nGo to http://localhost:8080/wp/wp-admin/plugins.php and activate the Auth0 plugin to start the setup. Select \"Standard Setup\".\n\nGo to Auth0 dashboard and collect these three pieces of information.\nThe exact details depend on the organization setup.\n* The tenant domain, eg. `YOUR-ORG.eu.auth0.com`\n* Auth0 application id of a \"MACHINE TO MACHINE\" application that\n  can create a suitable token for the Auth0 plugin, eg. `APP-ID`\n* Application secrect for this application, eg. `APP-SECRET`\n\nEg. in our organization the application \"Wordpress Auth0 plugin token generator\" is specifically setup for this purpose. Go to https://manage.auth0.com/#/applications to get the id and secret.\nSetting up this Auth0 m2m application is not covered in this documentation. Follow the Auth0 plugin documentation for instructions.\n\nGenerate a token for your Auth0 setup. This is a token that authorizes the Auth0 plugin to create the actual per-site configuration in Auth0.\n\n    curl --request POST \\\n      --url https://YOUR-ORG.eu.auth0.com/oauth/token \\\n      --header 'content-type: application/json' \\\n      --data '{\"client_id\":\"APP-ID\",\"client_secret\":\"APP-SECRET\",\"audience\":\"https://YOUR-ORG.eu.auth0.com/api/v2/\",\"grant_type\":\"client_credentials\"}'\n\nThe output of this command contains the necessary token.\n\n    {\"access_token\":\"TOKEN\",\"scope\":\"create:client_grants update:client_grants read:users update:users create:users update:clients create:clients read:connections update:connections create:connections delete:rules create:rules update:guardian_factors\",\"expires_in\":86400,\"token_type\":\"Bearer\"}\n\nInput the tenant domain and this token (`TOKEN` above) the Auth0\nplugin setup. You can skip social login and account migration steps.\nLog out and in to verify the Auth0 setup is working.\n\nThe application list at https://manage.auth0.com/#/applications\nshould now contain a new entry, created by the Auth0 plugin\nsetup wizard step described above. The name of the application is same as the title of the WP site you created. You *must* use descriptive names. Change the Auth0 application name if necessary. All sites and apps called just \"wp\", \"foo\" etc. may be deleted without any warning.\n\n## Note about google-app-engine plugin\n\n  Define this environment variable before activating the GAE specific google-app-engine plugin. This is required for `wp-cli`\n  to work after activating this plugin. This plugin uses APIs that are not available in the standard php include_path.\n\n    export WP_CLI_PHP_ARGS='-d include_path=vendor/google/appengine-php-sdk'\n\n  This is required every time and in every shell where you run\n  `wp-cli` with your local php, if/when this plugin is active.\n  This is *not* required for `dev_appserver.py` or anything else.\n\n  You can now activate rest of the plugins.\n\n## Create gcloud project and database for the deployment\n\n  Configure Google Cloud SDK with your account and the appropriate project ID. First time setup with `gcloud init`.\n\n  If you've already initialized \u0026 authorized the `gcloud` cli, create a new project and switch the gcloud configuration to use it with commands eg.\n\n    gcloud projects create some-very-descriptive-name\n    gcloud config set project some-very-descriptive-name\n\n  You can also create the project and database in the cloud console https://console.cloud.google.com/ but you _will_ need the cli to deploy, eventually.\n\n    gcloud sql instances create wp --activation-policy=ALWAYS --region=europe-north1\n\n## Connecting to the Cloud SQL instance\n\n  By default Cloud SQL instances are not accessible from external IP addresses. Instead of opening the access use `cloud_sql_proxy`. Download the proxy and install to some path in your $PATH. Do not commit it to the project repository.\n\n    curl -o cloud_sql_proxy https://dl.google.com/cloudsql/cloud_sql_proxy.darwin.amd64\n    chmod +x cloud_sql_proxy\n\n  Start the proxy. This will create UNIX sockets (in the given directory) for all Cloud SQL instances in your current project.\n\n    cloud_sql_proxy -dir /tmp\n\n  Leave it running in a separate terminal for as long as needed.\n  Now you can access the instances eg.\n\n    mysql -uroot -S /tmp/some-very-descriptive-name:europe-north1:wp\n\n  The naming convention is `\u003cproject name\u003e:\u003cregion\u003e:\u003cinstance name\u003e`. If you have multiple SQL instances in your project, the\n  proxy command above will create a socket for each.\n\n  Create the database and user just like for the localhost earlier. You can manage the users and databases also from the Cloud Console browser UI but here is the cli-way for consistency.\n\n    mysql -uroot -S /tmp/some-very-descriptive-name:europe-north1:wp \u003c\u003cEOD\n    \u003e create database wp;\n    \u003e create user 'wp' identified by 'wp';\n    \u003e grant all on wp.* to 'wp';\n    \u003e EOD\n\n  Setup `.env.gae` for GAE deployment.\n\n    DB_NAME=wp\n    DB_USER=wp\n    DB_PASSWORD=wp\n    DB_HOST=:/cloudsql/some-very-descriptive-name:europe-north1:wp\n    WP_ENV=production\n    WP_HOME=https://some-very-descriptive-name.appspot.com\n    WP_SITEURL=${WP_HOME}/wp\n\n## Database migration from localhost to Cloud SQL\n\n  We can initialize the Cloud SQL instance database with data\n  from the local development version. This includes the Auth0\n  setup, so we don't have to redo it.\n\n    vendor/bin/wp search-replace 'http://localhost:8080' \\\n      'https://some-very-descriptive-name.appspot.com' \\\n      --export=migrate-localhost-to-gae.sql\n\n    mysql -uroot \\\n      -S /tmp/some-very-descriptive-name:europe-north1:wp wp \\\n      \u003c migrate-localhost-to-gae.sql\n\n**NOTE! In the examples above we're using `wp` as instance name database name, user name and password. This may be confusing.**\n\n## Deploying to GAE\n\n  Define what files to ignore when deploying by listing those in\n  a .gitignore style file `.gcloudignore`. If ignore rules are not defined, GAE will use defaults that includes eg. _all_ dot-files. We want to deploy `.env.gae` (so the defaults do not work for us) but _not_ eg. `.env` (so we define it below).\n\n    echo '.env' \u003e.gcloudignore\n\n  Add this code in `config/application.php`, some place before\n  the Dotenv operation.\n\n    /*\n    * Detect if we're running on GAE\n    */\n    if (isset($_SERVER['SERVER_SOFTWARE']) \u0026\u0026 strpos($_SERVER['SERVER_SOFTWARE'], 'Google App Engine') !== false) {\n        $onGae = true;\n    } else {\n        $onGae = false;\n    }\n\n  Change the Dotenv code as follows. This will select the env-file\n  to load depending on the runtime (local development or GAE).\n\n    /**\n     * Use Dotenv to set required environment variables and load .env file in root\n     */\n    $dotenv_name = $onGae ? '.env.gae' : '.env';\n    $dotenv = new Dotenv\\Dotenv($root_dir, $dotenv_name);\n    if (file_exists($root_dir . '/' . $dotenv_name)) {\n        $dotenv-\u003eload();\n        $dotenv-\u003erequired(['DB_NAME', 'DB_USER', 'DB_PASSWORD', 'WP_HOME', 'WP_SITEURL']);\n    }\n\n  Add these definitions to `config/environments/production.php`.\n\n    define('WP_CACHE', true);\n    define('DISABLE_WP_CRON', true);\n\n  Deploy to GAE. First time deployment will ask the region.\n  Following deploys will use the same region and copy only\n  modified files. GAE does not perform build steps on the\n  cloud, so everything is copied as static files and must\n  include the build targets (eg. `vendor` and `web/wp`).\n\n    gcloud app deploy app.yaml\n\n  Site is now deployed and configured. Go to\n  https://some-very-descriptive-name.appspot.com/wp/wp-admin/\n  and try loggin with Auth0. Follow the instructions to add the login address to the list of allowed callback addresses of the associated Auth0 application. The same application is used for localhost development and was setup in the earlier steps. The error page has direct link to the correct configuration page in Auth0 dashboard, and shows the exact URL that needs to be added.\n  Add also site root URL to the list of Allowed Web Origins.\n\n  Go to https://console.cloud.google.com/logs/viewer?project=some-very-descriptive-name to view additional logs.\n  For example, if the project does not have billing enabled\n  (depending how the project was setup), the\n  outgoing http requests will fail. This shows in the log as error\n  \"The Socket API will be enabled for this application once billing has been enabled in the admin console.\"\n\n  Enable billing for the gcloud app, if necessary. Log in to wp-admin and go to https://some-very-descriptive-name.appspot.com/wp/wp-admin/options-general.php?page=appengine and set bucket name to empty (ie. use\n  the default as instructed on the page). This is necessary because\n  the config was copied from the local development environment.\n\n  Test the admin functionalities, eg. creating new posts and media\n  library items. Test batcache page caching in a separate incognito window.\n\n## Summary\n\n  To deploy what is already in this repository, here are the steps.\n\n    gcloud projects create some-very-descriptive-name\n    gcloud config set project some-very-descriptive-name\n    gcloud beta billing accounts list  ## locate the ID\n    gcloud beta billing projects link some-very-descriptive-name --billing-account=ID\n    gcloud sql instances create wp --activation-policy=ALWAYS --region=europe-north1\n\n    cloud_sql_proxy -dir /tmp\n\n    mysql -uroot -S /tmp/some-very-descriptive-name:europe-north1:wp \u003c\u003cEOD\n    \u003e create database wp;\n    \u003e create user 'wp' identified by 'wp';\n    \u003e grant all on wp.* to 'wp';\n    \u003e EOD\n\n    cat \u003e.env.gae \u003c\u003cEOD\n    \u003e DB_NAME=wp\n    \u003e DB_USER=wp\n    \u003e DB_PASSWORD=wp\n    \u003e DB_HOST=:/cloudsql/some-very-descriptive-name:europe-north1:wp\n    \u003e WP_ENV=production\n    \u003e WP_HOME=https://some-very-descriptive-name.appspot.com\n    \u003e WP_SITEURL=\\${WP_HOME}/wp\n    \u003e EOD\n\n    composer install\n    gcloud app deploy app.yaml\n    gcloud app browse  ## setup wp \u0026 activate plugins in the browser\n\n## Automating the build with Cloud Build\n\n  Define the build configuration in `cloudbuild.yaml`\n\n    steps:\n    - name: 'composer'\n      args: ['install']\n    - name: gcr.io/cloud-builders/gsutil\n      args: ['cp', '-r', 'gs://${PROJECT_ID}_build_dropin/*', '.']\n    - name: 'gcr.io/cloud-builders/gcloud'\n      args: ['app', 'deploy']\n\n  Create the build configuration / drop-in bucket and\n  copy your `.env.gae` there.\n\n    gsutil mb -l eu gs://some-very-descriptive-name_build_dropin\n    gsutil cp .env.gae gs://some-very-descriptive-name_build_dropin\n\n  Clean up the repo of all already generated build assets.\n  Test automated build locally. Setting up `cloud-build-local` requires Docker and some setup. See Google Cloud documentation for details.\n\n    git clean -fdx\n    cloud-build-local --dryrun=false .\n\n  Before submitting the build to Cloud Build, enable the\n  App Engine Admin API and Cloud Build API for your app and authorize the\n  Cloud Build service account to roles `App Engine Deployer`\n  and `App Engine Service Admin`. Without these permissions\n  the Cloud Build can't deploy new versions and setup traffic\n  splitting to the new version.\n\n    gcloud services enable appengine.googleapis.com\n    gcloud services enable cloudbuild.googleapis.com\n    gcloud projects get-iam-policy some-very-descriptive-name\n\n    # locate the NNN@cloudbuild.gserviceaccount.com member\n    # that has been granted roles/cloudbuild.builds.builder\n    # NNN is the project number, so you may know it already\n\n    gcloud projects add-iam-policy-binding some-very-descriptive-name \\\n      --member serviceAccount:***@cloudbuild.gserviceaccount.com \\\n      --role roles/appengine.deployer\n    gcloud projects add-iam-policy-binding some-very-descriptive-name \\\n      --member serviceAccount:***@cloudbuild.gserviceaccount.com \\\n      --role roles/appengine.serviceAdmin\n\n  Submit the build to the Cloud Build\n\n    gcloud builds submit --config cloudbuild.yaml .\n\n  Finally, you can setup build trigger for Github to trigger\n  the build automatically on push https://console.cloud.google.com/cloud-build/triggers?project=wp-on-gae-ci\n\n  Google Cloud Build app for Github https://github.com/apps/google-cloud-build can be used\n  to build multiple selected or all repositories with one\n  Google Cloud project's Cloud Build. This is suitable for eg. CI testing, but project specific deployment (like configured above) would need another solution.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrc%2Fbedrock-on-gae","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffrc%2Fbedrock-on-gae","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrc%2Fbedrock-on-gae/lists"}