Important Notice
This project uses the
@apostrophecms-pro/multisiteand@apostrophecms-pro/multisite-dashboardmodules, which require an Apostrophe Assembly license. To obtain a license, visit apostrophecms.com/assembly or contact the Apostrophe team. If you want a license-free starting point instead, see the open-source starter kits at apostrophecms.com/starter-kits.
This repository is a working, multisite-enabled ApostropheCMS project you can run locally to evaluate Apostrophe Assembly. It is a single codebase that serves:
- A dashboard site (
dashboard/) whose only job is to create and manage the other sites. - Any number of content sites (
sites/), all sharing the same project code but with independent databases and content.
It also serves as example code for organizing a multisite project and writing your own custom modules and widgets.
In a normal single-site Apostrophe project, you configure modules in app.js. In a
multisite project the layout is different:
| Path | Role |
|---|---|
app.js |
Top-level configuration shared by all sites (database host, port, hostname prefix, session secret, etc.). |
sites/index.js |
The equivalent of a single-site project's app.js — module configuration for your content sites. |
sites/modules/ |
Project-level modules for your content sites (the equivalent of a single-site modules/). |
dashboard/index.js, dashboard/modules/ |
The same idea, for the management dashboard. |
domains.js |
Base domains for each environment (dev/staging/prod), used to build site hostnames. |
themes.js |
The list of available site themes. |
In all other respects, development is ordinary ApostropheCMS development. If you are new to Apostrophe, start with the ApostropheCMS documentation.
In app.js, shortNamePrefix is currently public-demo-. Change it to a unique
string for your project (conventionally your repo name followed by -). This
prefixes the MongoDB database names so they don't collide with other Assembly
projects in development.
If you self-host on a low-end MongoDB Atlas cluster (below M10), keep the prefix under 12 characters before the
-.
Edit domains.js so the values match your real domains. Leave local set to
localhost:3000 for development. staging and prod are placeholders
(staging.example.com, example.com) — replace them with real domains backed by a
wildcard DNS record and a wildcard TLS certificate if you self-host. The first key
in the object is treated as the local development environment.
Each site you create gets a shortname and is automatically reachable as a
subdomain of every configured domain (e.g. company1.localhost:3000,
company1.staging.example.com).
By default the dashboard lives on the dashboard subdomain. Override it with
dashboardShortName in app.js (or the APOS_DASHBOARD_SHORTNAME environment
variable).
Before any real deployment, replace the placeholder secrets with your own random strings:
sessionSecretinapp.jssecret(Express session) insites/index.jssecretindashboard/modules/@apostrophecms/express/index.js(currently literallyCHANGEME)disabledFileKeyinsites/index.jsanddashboard/index.js(used to disable access to files in the local uploads backend)
The local requirements are the same as for single-site ApostropheCMS. See the Apostrophe development setup guide for details.
Because this project serves multiple hostnames, subdomains must resolve to your
machine. In Chrome only, every *.localhost subdomain already resolves to
127.0.0.1, so you can skip this at first. In other browsers, add an entry per site
you test, for example:
127.0.0.1 dashboard.localhost company1.localhost
Install dependencies:
npm install
Add an admin user to the dashboard site (it manages all the others). You'll be prompted for a password:
node app @apostrophecms/user:add admin admin --site=dashboard
When running command-line tasks in a multisite project you must always say which site you mean. Use
--site=dashboardfor the dashboard, any of a site's valid hostnames for an individual site (e.g.--site=company1.localhost), or--all-sitesto run a task on every site except the dashboard.
Launch the application:
npm run dev
Then visit:
http://dashboard.localhost:3000/login
Log in with the admin account you just created. From the Sites menu in the admin bar, add a new site. Sites are Apostrophe pieces in the dashboard:
- Give it a distinct shortname such as
company1. - Fill in the site's admin password field — this provisions an
adminuser on the new site.
After saving, log into the site directly:
http://company1.localhost:3000/login
Create company2, company3, and so on. The code is shared, but each site's
database and content are independent.
Logged out, you'll only see content that has been published ("Commit"/"Publish") on a site.
npm run build
This builds the dashboard assets and then builds assets once per theme via
scripts/for-each-theme. npm start runs the app without nodemon
(npm run production-start sets NODE_ENV=production).
Put the code for your content sites in sites/, exactly as you would in a
single-site project's app.js/modules:
- Configure and enable modules in
sites/index.js. - Add project modules under
sites/modules/.
This project sets nestedModuleSubdirs: true, so modules can be grouped in
subfolders. It also npm installs and configures @apostrophecms/blog,
@apostrophecms/seo, @apostrophecms/import-export, and others — see
sites/index.js.
The sites/modules folder contains example custom code you can adapt or delete,
including widget modules (hero-widget, card-widget, price-card-widget,
button-widget, article-widget, github-prs-widget, plus layout widgets) and
piece/page types (article, article-category, article-page, default-page).
Project-wide frontend JavaScript and Sass live in sites/modules/asset/ui/src.
github-prs-widget renders pull requests from a GitHub repository (it defaults to
apostrophecms/apostrophe). This is just a code example and you don't have to keep it. If you do, be aware that unauthenticated GitHub API requests are rate-limited;
to raise the limit, supply a personal access token as a module option in
sites/index.js:
'github-prs-widget': {
options: {
token: process.env.GITHUB_TOKEN
}
}Each site selects a single theme in the dashboard. This project ships one theme,
default, listed in themes.js:
export default [
{
value: 'default',
label: 'Default'
}
];A theme value is a permanent shortname and must not change once in use. Each theme
has a matching sites/lib/theme-<value>.js file that can enable extra modules or
tweak configuration for that theme. For default, that's sites/lib/theme-default.js.
To add a theme, add an entry to themes.js and create the corresponding
sites/lib/theme-<value>.js. Keeping themes.js accurate matters because
npm run build builds assets once per listed theme.
You can place a theme's frontend assets in the ui/src/index.js and
ui/src/index.scss of a module named after the theme. Static files (fonts,
favicons, etc.) placed in sites/public are served from /.
For best results, avoid creating one theme per site. Themes should be used for groups of sites with similar feature requirements.
The dashboard exists only to manage sites, so it doesn't need to be public-facing.
It is extended just like a content site, but its code lives in dashboard/. The
key module is site (dashboard/modules/site/index.js), a piece type with one
piece per managed site. It is provided by @apostrophecms-pro/multisite-dashboard
and configured here with the available themes, the baseUrlDomains, and
localizedSites: true.
Site schema field values are passed to each site as the site object in
sites/index.js (this is how the selected theme reaches each site). To expose
additional dashboard-controlled settings to sites, add fields to the site piece
and pass them through as module options in sites/index.js.
A site's database name is the prefix followed by the site piece's _id, which is
awkward to look up. Utility tasks are provided:
# Mongo shell for the dashboard
node app mongo:mongo --site=dashboard
# Mongo shell for an individual site (use its hostname)
node app mongo:mongo --site=company1.localhost
# mongodump / mongorestore
node app mongo:mongodump --site=company1.localhost
node app mongo:mongorestore --site=company1.localhost -- --drop
-- by itself marks the end of Apostrophe's options, so flags like --drop are
passed through to the underlying tool.
We offer and recommend our hosting, which provides easy deployment via git push and high availability, scalability and durability via AWS EC2, AWS S3 and MongoDB Atlas. Self-hosting is also fully supported, reach out to us for sample scripts. For production you must configure a cloud upload backend (such as Amazon S3) and a managed MongoDB — do not rely on the local filesystem for uploads across multiple instances. Contact the Apostrophe Assembly team or see the Assembly documentation for current hosting and deployment options.