Deploy to CMS SaaS
Why deployment works differently on SaaS
Section titled “Why deployment works differently on SaaS”On CMS PaaS, deploying means shipping a .NET application to your servers. Content types, rendering code, custom logic, and configuration all travel together in a single artifact. You control the entire pipeline — CI/CD, infrastructure, rollback.
CMS SaaS inverts this model. Optimizely runs the CMS infrastructure. You deploy your content model (types, configurations) to the Optimizely cloud, and your frontend application deploys separately to your own hosting. These two deployments are independent — you can update the content model without touching the frontend, and vice versa.
This separation requires a different mental model for deployment. You are not deploying an application; you are pushing definitions to a managed platform and connecting your frontend to the content it exposes through Graph.
What you will do
Section titled “What you will do”- Install and authenticate the Optimizely CLI
- Understand the environment structure (development, staging, production)
- Deploy content types and configuration to a development environment
- Promote deployments from development to staging to production
- Set up CI/CD integration for automated deployments
Install and authenticate the CLI
Section titled “Install and authenticate the CLI”The Optimizely CLI is the primary tool for managing CMS SaaS deployments.
# Install the Optimizely CLI globally
npm install -g @optimizely/cli
# Verify installation
optimizely --version
# Authenticate with your CMS SaaS account
# This opens a browser for OAuth login
optimizely login
# Verify authentication and list available environments
optimizely environments list Authentication tokens are stored locally. For CI/CD pipelines, use API key authentication instead of browser-based login.
# Authenticate with an API key (for CI/CD environments)
export OPTIMIZELY_API_KEY="your-api-key-here"
optimizely environments list Understand the environment structure
Section titled “Understand the environment structure”CMS SaaS provides isolated environments for each stage of your workflow.
| Environment | Purpose | Content data | Content types |
|---|---|---|---|
| Development | Build and test new content types, try configurations | Test content only | Latest development definitions |
| Staging | Integration testing, content preview, UAT | Copy of production content (via sync) or separate test content | Promoted from development |
| Production | Live site serving real visitors | Real content | Promoted from staging |
Key principle: Content types flow upward (dev to staging to production). Content data can be synced downward (production to staging) for realistic testing. Never push untested type definitions directly to production.
Deploy content types
Section titled “Deploy content types”Content type manifests are JSON files that describe your content model. You deploy them with the CLI.
# Project structure
# my-cms-project/
# content-types/
# HeroBlock.json
# LandingPage.json
# ArticlePage.json
# config/
# settings.json
# Step 1: Validate manifests locally (catches errors before deployment)
optimizely content-types validate ./content-types/
# Step 2: Deploy to development environment
optimizely content-types push ./content-types/ --environment development
# Step 3: Verify deployment
optimizely content-types list --environment development
# Step 4: Check a specific type's deployed definition
optimizely content-types get HeroBlock --environment development What happens during deployment
Section titled “What happens during deployment”- The CLI reads your manifest files and validates them against the platform schema
- Each type definition is compared against the existing definition in the target environment
- New types are created; modified types are updated in place
- The CMS editor immediately reflects the changes — no restart needed
Promote across environments
Section titled “Promote across environments”Promotion is the process of moving validated definitions from one environment to the next.
# After validating in development, promote to staging
optimizely content-types push ./content-types/ --environment staging
# Run integration tests against staging
# (your test suite verifies Graph queries return expected shapes)
# After staging validation, promote to production
optimizely content-types push ./content-types/ --environment production
# Verify production deployment
optimizely content-types list --environment production Promotion checklist:
- All content types validate without errors
- Test content created in the development environment exercises each type
- Visual Builder works correctly for all page types with sections
- Graph queries from your frontend return the expected data shape
- No breaking changes to existing published content (properties renamed or removed)
Manage environment configuration
Section titled “Manage environment configuration”Beyond content types, environments have their own configuration for features like localization, webhooks, and Graph settings.
# Export current configuration from an environment
optimizely config export --environment development > config/dev-settings.json
# Push configuration to an environment
optimizely config push ./config/settings.json --environment development
# Sync content from production to staging for realistic testing
optimizely content sync --from production --to staging Set up CI/CD integration
Section titled “Set up CI/CD integration”Automate deployments by integrating the CLI into your CI/CD pipeline.
name: Deploy CMS Content Types
on:
push:
branches: [main]
paths:
- 'content-types/**'
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Optimizely CLI
run: npm install -g @optimizely/cli
- name: Validate content types
run: optimizely content-types validate ./content-types/
- name: Deploy to staging
env:
OPTIMIZELY_API_KEY: ${{ secrets.OPTIMIZELY_API_KEY_STAGING }}
run: optimizely content-types push ./content-types/ --environment staging
- name: Deploy to production
if: github.ref == 'refs/heads/main'
env:
OPTIMIZELY_API_KEY: ${{ secrets.OPTIMIZELY_API_KEY_PRODUCTION }}
run: optimizely content-types push ./content-types/ --environment production CI/CD best practices:
- Store API keys as secrets — never commit them to source control
- Validate before deploying — fail the pipeline early if manifests have errors
- Deploy to staging first — even in automated pipelines, run integration tests before production
- Use branch protection — require pull request reviews for changes to content type manifests
- Version your manifests — track content type definitions in Git alongside your frontend code
Rollback strategy
Section titled “Rollback strategy”If a deployment introduces problems:
- Revert the manifest change in Git and redeploy the previous version
- Content type updates are non-destructive — reverting a property addition hides the property but does not delete content
- Breaking changes (removing a required property that content depends on) should be handled with a migration plan, not a rollback
There is no one-click “undo deployment” button. Your rollback strategy is your Git history plus a redeploy of the previous manifest version.
Common issues
Section titled “Common issues”| Issue | Cause | Fix |
|---|---|---|
| CLI authentication fails | Expired token or invalid API key | Run optimizely login again, or regenerate the API key |
| Push fails with conflict | Two developers pushed conflicting type changes | Pull the latest deployed definition, merge changes, push again |
| Types not appearing in editor | Deployment targeted wrong environment | Verify with environments list and content-types list |
| Graph queries return old schema | Graph schema cache not refreshed | Wait a few minutes for cache invalidation, or trigger a manual refresh |
| Content sync fails | Insufficient permissions on source environment | Ensure your API key has read access on the source and write access on the target |