Developing your site
Your instance is tuned for production. Caches are on, aggregation is on, errors are hidden, and a fast cache backend (Valkey) sits in front of the database. That is exactly what you want for a live site and exactly what gets in your way when you are building a theme or a module and need to see every change immediately.
The good news: you don't have to fight any of that, and you never need root or your host
to do it. BOA gives you a development URL for every site, and everything you do on
this page happens in your own shell (as oN.ftp) with Drush, plus a couple of files you
are allowed to edit. Let's walk through it.
The .dev. preview URL
The trick that makes all of this pleasant is a special address. When you visit your site
through a hostname that has .dev. in it, BOA quietly switches that page into
development mode for you:
- errors are shown on screen instead of hidden,
- the internal page cache and CSS/JS aggregation are turned off,
- opcode caching revalidates on every request, so edited PHP is picked up straight away.
Your normal live address is untouched the whole time. Real visitors keep seeing the fast,
cached, error-free production site. Only requests that come in through the .dev. name
get the development treatment. That means you can debug on the same running site, with the
same database and files, without putting the live site into a fragile state.
Turning the development URL on
The development URL isn't magic wiring you have to request from anyone. It is just an
alias of your site whose name contains .dev., and you add it yourself on the site's
edit form in the control panel, the same place you add any other alias (see
Site aliases and redirects).
Add an alias in this shape, with dev sitting between two dots:
www.dev.example.com
The .dev. in the middle is what both BOA's front end and the web server look for. A name
like dev.example.com (with dev only at the start) will also flip the page into
development mode, but the reliable, always-served form is the one with .dev. in the
middle, so prefer www.dev.example.com or staging.dev.example.com.
A few practical notes:
- Point the DNS for that name at your instance just like any other alias, or add it to
your workstation's
hostsfile if it is only for your own eyes. - If your site has SSL enabled, the
.dev.alias is covered too. BOA folds any alias with.dev.(or.devel.) in the middle into the site's HTTPS server and into its automatic Let's Encrypt certificate, sohttps://www.dev.example.comjust works — no separate certificate to request. (The dev/devel exception only skips certificates when a site's main name is a dev name, not for a.dev.alias of a normal site.) - Nothing about the production site changes. You are adding a second door, not rebuilding anything.
The .dev. debug headers — read your live settings at a glance
Responses on a .dev. name carry a family of X-* debug headers that exist
only there — never on your live URLs. The most useful set is X-Ini-*,
which makes reading your effective INI configuration easy: one header per
active value, plus X-Ini-Loc-Src and X-Ini-Plr-Src naming the exact site
or platform file that supplied it (X-Ini-Src: BOA-Default means neither is
set). X-Valkey-Prefix shows the cache namespace the site resolved to, and
X-Base-Url / X-Site-Subdir the URL plumbing:
curl -sI https://www.dev.example.com/ | grep -i x-ini
The one exception to only-on-.dev. runs the other way: the X-Cache-*
verify headers (see
the object cache) are gated
by the redis_debug_header INI setting instead, precisely so you can check
caching on your production URLs.
Search engines can't see it — but treat the name as a secret anyway
You don't need to worry about your development URL leaking into search results. The web server recognizes the known crawlers by their user-agent — Google, Bing, Yandex, Baidu, and the generic bot/spider/crawler signatures — and answers them with a plain 404 on any dev-style hostname. To a search engine, the development view of your site simply doesn't exist, so nothing from it gets indexed.
People are a different matter. Development mode puts PHP errors on the page, and errors can reveal more than you'd like — file paths, module names, sometimes a fragment of configuration. Anyone who knows (or guesses) the dev name sees all of it, from anywhere. Two easy habits keep that a non-issue:
- Treat the dev name like a password — don't publish or share it beyond
the people doing the work. If it only ever needs to work for you, skip
public DNS entirely and put the name in your workstation's
hostsfile. - Delete the alias when a stretch of development work is done. It's one line removed on the site's edit form, and re-adding it later takes seconds. A dev door that doesn't exist can't be found.
Twig debugging and turning off the cache backend (Drupal 8+)
On the .dev. URL, BOA already relaxes Drupal's page cache and aggregation for you. Two
things it does not turn on by itself, because they are choices only you should make,
are Twig template debugging and bypassing the Valkey cache backend. You switch both on with
one small file that you drop into your own site's files directory.
Create this file (from your oN.ftp shell, or over SFTP):
sites/example.com/files/development.services.yml
with these contents:
parameters:
http.response.debug_cacheability_headers: true
twig.config:
debug: true
auto_reload: true
cache: true
services:
cache.backend.null:
class: Drupal\Core\Cache\NullBackendFactory
Here is what each part buys you, and what BOA does around it:
- The
services:block defines a "do nothing" cache backend. Just having this file present on a.dev.request tells BOA to load it and to route Drupal's render and dynamic page caches through that null backend instead of Valkey. So while you develop on the.dev.URL your edits aren't served from the cache layer, and you don't have to touch any cache configuration yourself. twig.config: debug: truemakes Drupal annotate the rendered HTML with the template file names and theme suggestions for every region and block, which is the whole point of Twig debugging.- Leave
auto_reload: trueandcache: trueas shown. BOA's own guidance is to keep the Twig cache on:auto_reloadalready re-renders a template the moment its source changes, and turning the cache fully off just makes every page slower for no extra benefit.
One catch to remember. After you first add (or later remove) this file you must clear the
site's caches once, or the .dev. URL can answer with an error 500 complaining about a
missing cache.backend.null service. Clear caches either from the control panel with the
Flush all caches task on the site, or from your shell:
drush @example.com cr
Then load https://www.dev.example.com and you'll see the Twig annotations in the page
source.
Theme debugging on Drupal 7
Drupal 7 has no Twig, so the file above doesn't apply. The .dev. URL still relaxes caching
and shows errors for you exactly the same way. For template-level debugging, Drupal 7 has its
own switch you can flip with Drush from your shell:
drush @example.com vset theme_debug 1
drush @example.com cc all
Theme hook suggestions now appear as HTML comments around each rendered element. Turn it back off when you're done:
drush @example.com vset theme_debug 0
drush @example.com cc all
Sass / Compass: Ruby tooling in your shell
If your theme builds its CSS with Sass or Compass, you can run that toolchain
directly in your oN.ftp shell — no compile-locally-and-upload loop. Ruby and
the gem machinery are provided by the server; you switch them on for your
account with a single control file:
touch ~/static/control/compass.info
Then log out, give it up to five minutes, and log back in. The tooling is installed for your account in the background, and a fresh login picks up the new paths. A few gems can't build their native extensions inside the limited shell, so BOA pre-installs those problem gems for you automatically as part of that setup — you don't have to do anything about them. From there it's ordinary gem work:
gem install --conservative compass
cd ~/platforms/my-platform/sites/all/themes/mytheme
bundle install # if the theme ships a Gemfile — Bundler comes preinstalled
compass watch # or: compass compile
gem-list shows the gems your account has — run it after your account is
initialized, though: before then it can still show an old, system-wide gem
list, and only once your account is set up does it list just your own local
Ruby and gems. Run the tools directly (compass watch, guard start) rather
than through a Drush wrapper — wrappers that open a sub-shell (like drush omega-guard) don't work in the limited shell.
The control file enables Ruby for your main account and any client
sub-accounts on your instance — and because each sub-account gets its own
copy of the gems, initialization takes a little longer the more sub-accounts
you have. Deleting ~/static/control/compass.info removes the gems from all
of them again a few minutes later.
A few notes:
- If you've been around BOA long enough to remember RVM: it's gone. The old per-user RVM setup was replaced by a single server-provided Ruby, so any guide that begins "install RVM" no longer applies — the control file above is the entire setup.
- If
gemstill isn't available well after five minutes and a fresh login, the Ruby toolchain may simply not be enabled on your server — it's an optional server-side extra. A short support request to your host sorts that out. - If
bundle installsays it can't build a native extension for a gem thatgem-listalready shows as installed, compare the versions — the copy BOA pre-installed often differs from the one pinned in your theme'sGemfile.lock. Point the lock file at the installed version and runbundle installagain, or reinitialize Ruby to land on a different build: delete~/static/control/compass.info, wait untilcompass --versionstops working, then touch the file again.
What you may edit, and what BOA rewrites
This is the part people trip on most often, so it's worth being clear. The site's PHP settings files are kept under your instance's own control, and they are reset for you on a schedule. Knowing which file to reach for saves a lot of head-scratching.
Leave these alone. BOA owns them and rewrites them:
sites/example.com/settings.php— regenerated every time the site is verified. It's the glue that wires the site to its database, its cache, and the shared BOA settings. Editing it is temporary at best and can break the site at worst.
Your first stop for a setting change: the site control file.
Most of the per-site knobs you'd once have reached into PHP for now live in a plain, commented INI file that is genuinely yours to edit from your shell or over SFTP:
sites/example.com/modules/boa_site_control.ini
BOA drops this file in for you the first time it tidies the site, group-writable so you can
open it and change values. It's where you toggle things like which cache bins skip the
cache backend (redis_exclude_bins), or turn the backend off site-wide
(redis_cache_disable), without touching any PHP. These keys keep the redis_ prefix even
on a server running Valkey — BOA uses a single redis_ namespace for both, so don't go
looking for a valkey_ toggle. Open the file, read the comments, uncomment the line you
want, and save. This is the supported, no-surprises place for a hosted site's overrides.
About local.settings.php and drushrc.php. These two PHP files also exist in your
site directory, and BOA does not overwrite their contents — but it does reset their
permissions back to read-only on every verify and again overnight, and they're owned by the
site's system user rather than your oN.ftp shell user. So they are not files you can just
open and edit the way you can the INI above. settings.php includes local.settings.php
last, after everything else, which is what makes it the ultimate override point. To
change it yourself, follow BOA's supported procedure: create the control file
sites/example.com/modules/local-allow.info, run the Reset password task to make
local.settings.php group-writable, edit it, test as admin, then run Verify to
restore permissions — or just describe the override you need and open a support request. Take care: a PHP error in
local.settings.php that you haven't verified as admin first can cause a permanent white
screen. For the everyday settings, reach for boa_site_control.ini first — that's the one
built for you to change.
There's a fuller tour of which file to reach for in Which file do I edit?.
Your actual code — the modules and themes under sites/all/ or sites/example.com/, and
anything in a platform you built yourself — is entirely yours to edit in place. BOA only
manages the settings wrappers; it doesn't rewrite your modules, themes, or libraries.
Where your own modules and themes belong
On a platform you built yourself, this question answers itself: it's your codebase,
so code goes into the codebase — composer require it, or commit it to your repo,
and rebuild the platform when you update. The interesting case is a built-in
platform — one your host provides — because there you can't touch most of the
tree, and what you can touch comes in two flavours with very different behaviour.
What's locked, and what's open. On a built-in platform, Drupal core and the
installation profile are read-only and root-owned — you can't overwrite a core
file, patch core, or edit the profile, and that's enforced, not just discouraged.
What stays writable for you is the sites/ tree: the shared
sites/all/modules, sites/all/themes and sites/all/libraries folders, plus
each of your sites' own sites/example.com/ folders. If your project genuinely
needs a patched core, that's the signal you've outgrown built-in platforms —
build your own instead (Building your own platform),
where the whole tree is yours.
sites/all — the platform-wide level, and how overriding works. Code you put
under sites/all/modules is seen by every site on the platform, and thanks to
Drupal's own precedence rules it wins over code shipped deeper in the platform.
That's the supported way to override one of the extra contrib modules BOA bundles
with its built-in platforms, Drupal 6 through 11 (they live in shared o_contrib*
areas, deliberately outside the profiles): upload your preferred version of the
module to sites/all/modules/ and Drupal uses yours, no server-side changes needed.
After adding or overriding code, rebuild. Drupal caches where every module
lives, so freshly placed code isn't picked up until that map is rebuilt. The
control panel gives you one button that does the right thing on every Drupal
version: the Rebuild registry task on the site rebuilds the registry on
Drupal 6/7, and on Drupal 8 and newer rebuilds the compiled service container and
flushes every cache. From your shell, drush @example.com cr (Drupal 8+) or
drush @example.com cc all (Drupal 7) covers the everyday cases.
sites/example.com/modules — the site-local level, with one big caveat. Each
site's own folder also has modules, themes and libraries subfolders, created
for you writable on purpose — this is a supported place for code, and the right
one for anything truly specific to a single site (a site-only glue module, a theme
no other site uses). It's also how you can test a module or theme on one site of
a shared platform without touching the others, since the site level takes
precedence over sites/all.
The caveat: site-local code travels with the site, and no platform move ever
updates it. When a site is cloned or migrated, its whole sites/example.com/
folder — modules included — is carried to the destination as-is. Migrate the site
onto a shiny new platform and the new codebase is current everywhere except
that folder: whatever versions you parked there follow the site around, ageing
quietly, entirely yours to remember and update by hand. An outdated site-local
copy also shadows the newer copy a platform may ship — the precedence that makes
overrides work cuts both ways. So keep the site level for genuinely site-specific
code and short-lived tests; anything shared, security-sensitive, or meant to be
refreshed when platforms are upgraded belongs at sites/all — or better, baked
into a platform you build and rebuild yourself.
Getting the aggregation toggle back on the live URL
On the .dev. URL, CSS/JS aggregation is already off for you. But one classic
situation needs it off (or at least toggleable) where your visitors are:
a theme or module ships styles or scripts that only misbehave when aggregated,
and until it's fixed you want to flip aggregation off from Drupal's admin
Performance page — on the live URL.
Out of the box that toggle appears to do nothing. BOA hardcodes aggregation
on for every non-dev request, deliberately, so a site can't quietly run slow
with aggregation forgotten off. The override is still self-service, though,
and it lives in local.settings.php — the file from the section above, which
loads after all of BOA's shared settings and therefore has the last word.
Following the local.settings.php editing procedure above, add these lines —
on Drupal 6/7:
unset($conf['preprocess_css']);
unset($conf['preprocess_js']);
or on Drupal 8 and newer:
unset($config['system.performance']['css']['preprocess']);
unset($config['system.performance']['js']['preprocess']);
Note what this does: it doesn't switch aggregation off — it stops BOA from forcing it on, which hands control back to the checkboxes on the Performance admin page. Toggle them there as your fix-up work needs. When the underlying CSS/JS problem is solved, remove the lines again (the same edit procedure) so the enforced default can keep protecting the site's speed.
A comfortable loop
Putting it together, a typical session looks like this:
- Once, add a
www.dev.example.comalias to the site so you have a preview URL. - Once, if you want Twig debugging and a cache-free preview on Drupal 8+, drop in
development.services.ymland rundrush @example.com cr. - Edit your theme or module code in place from your shell or over SFTP.
- Refresh
https://www.dev.example.comto see the change with full errors and template hints. For a config or Twig change that isn't showing,drush @example.com cr(orcc allon Drupal 7) and refresh again. - When you're happy, the live URL already has your code — it's the same site. Just take the
development bits back off: remove
development.services.yml(and clear caches), or settheme_debug 0on Drupal 7.
If you ever want a throwaway copy to experiment on instead of your live site, clone the site
first — see Cloning and migrating —
and point a .dev. alias at the clone.
When something needs a change at the server level that these files and tasks can't reach — a PHP extension that isn't installed, a resource limit, anything outside your own instance — that's something your host does. Open a support request and describe what you need.
Related
- Building your own platform — bringing your own codebase in as a platform to develop against.
- Drush basics — the
drush @site cr/cc allcommands used throughout this page. - Which file do I edit? — the full map of tenant- editable files versus BOA-managed ones.
- Site aliases and redirects — how
to add the
.dev.alias that powers your preview URL. - Something looks broken — if dev mode turns up a real problem on the site.