Deploy Classic UI Example

This guide shows you how to deploy the Classic UI example to your Kubernetes cluster.

What You’ll Deploy

The Classic UI example provides traditional Plone with server-side rendering:

  • Plone 6.1 Classic UI (traditional interface, no separate frontend)

  • PostgreSQL with RelStorage (CloudNativePG or Bitnami)

  • Varnish HTTP caching with kube-httpcache

  • Ingress with TLS (Traefik or Kong)

  • Simpler architecture (single backend service)

Classic UI vs Volto

Feature

Classic UI

Volto

Architecture

Single backend

Frontend + Backend

Rendering

Server-side

Client-side (React)

Deployment

Simpler

More complex

Best for

Migrations, intranets

Modern projects

Tip

Choose Classic UI if you’re migrating from older Plone versions or need specific Classic UI add-ons. For new projects, consider Volto.

Prerequisites

Same as the Production Volto guide, you need:

  • Ingress controller (Traefik or Kong)

  • cert-manager

  • kube-httpcache operator

  • PostgreSQL operator (CloudNativePG or Bitnami)

See Setup Prerequisites for detailed instructions.

Step 1: Get the Example

git clone https://github.com/bluedynamics/cdk8s-plone.git
cd cdk8s-plone/examples/classic-ui

Step 2: Install Dependencies

npm install

Step 3: Import CRDs

npm run import

Step 4: Configure Environment

Create .env from the example:

cp .env.example .env

Edit .env:

# Your domains
DOMAIN_CACHED=plone.example.com
DOMAIN_UNCACHED=plone-test.example.com
DOMAIN_MAINTENANCE=plone-admin.example.com

# Your cert-manager ClusterIssuer
CLUSTER_ISSUER=letsencrypt-prod

# Optional: Custom backend image
#PLONE_BACKEND_IMAGE=plone/plone-backend:6.1.3

# Database: 'bitnami' or 'cloudnativepg'
DATABASE=cloudnativepg

Note

Classic UI only needs one image (backend). There’s no frontend image configuration.

Step 5: Generate Manifests

npm run synth

Creates dist/plone-classic.k8s.yaml (~27 KB, smaller than Volto’s 32 KB).

Step 6: Review Manifests

# Count resources
grep "^kind:" dist/plone-classic.k8s.yaml | sort | uniq -c

# Dry run
kubectl apply --dry-run=client -f dist/plone-classic.k8s.yaml

Step 7: Deploy

kubectl apply -f dist/plone-classic.k8s.yaml

Or to a specific namespace:

kubectl apply -f dist/plone-classic.k8s.yaml -n plone

Step 8: Monitor Deployment

# Watch pods
kubectl get pods -l app.kubernetes.io/part-of=plone -w

# Wait for ready
kubectl wait --for=condition=ready pod \
  -l app.kubernetes.io/part-of=plone \
  --timeout=300s

Note

Classic UI deploys fewer pods than Volto (no frontend pods).

Step 9: Verify Services

kubectl get svc -l app.kubernetes.io/part-of=plone

You should see:

  • plone-backend (Classic UI service)

  • plone-httpcache (Varnish cache)

  • Database service

Step 10: Check Ingress

kubectl get ingress
kubectl get certificate

Step 11: Access Your Site

Once DNS and TLS are ready:

  • Public (cached): https://plone.example.com

  • Testing (uncached): https://plone-test.example.com

  • Maintenance: https://plone-admin.example.com

Create Plone Site

  1. Access maintenance domain: https://plone-admin.example.com

  2. Click “Create a new Plone site”

  3. Configure:

    • Site ID: Plone

    • Title: Your site name

    • Language: Select language

    • Add-ons: Choose Classic UI add-ons

  4. Click “Create Plone Site”

Key Differences from Volto

Architecture

Classic UI routing is simpler - all traffic goes to the backend:

Traffic → Ingress → Varnish → Plone Backend (Classic UI)

Compared to Volto:

Traffic → Ingress → {Varnish → Frontend, Backend}

Ingress Routes

Classic UI uses virtual host rewriting for direct backend access:

  • Cached: Routes through Varnish to backend

  • Uncached: Direct to backend with VirtualHostBase rewrite

  • Maintenance: Direct backend access with VirtualHostRoot

No Frontend Service

The manifest doesn’t include:

  • Frontend deployment

  • Frontend service

  • Frontend-to-backend internal routing

This makes the deployment ~15% smaller and simpler to manage.

Troubleshooting

Backend Not Starting

Check backend logs:

kubectl logs -l app.kubernetes.io/name=plone-backend -f

Common issues:

  • Database connection failures

  • Memory limits too low

  • Image pull errors

Classic UI Interface Not Loading

  1. Check if backend pods are running:

    kubectl get pods -l app.kubernetes.io/name=plone-backend
    
  2. Verify virtual host rewriting in ingress:

    kubectl describe ingress
    
  3. Check Varnish routing:

    kubectl logs -l app.kubernetes.io/name=plone-httpcache
    

Add-on Compatibility

Some add-ons are Volto-specific. For Classic UI:

  • Use Classic UI themes (not Volto themes)

  • Check add-on compatibility with Plone 6 Classic UI

  • Avoid Volto-specific frontend add-ons

Migrating to Volto

If you want to migrate from Classic UI to Volto later:

  1. Keep your backend deployment (same configuration)

  2. Add Volto frontend from the Volto example

  3. Update ingress to route to frontend

  4. Both UIs can run simultaneously during migration

See the Production Volto deployment guide for details.

Customization

Backend Configuration

Edit main.ts to customize:

const plone = new Plone(this, 'plone', {
  variant: PloneVariant.CLASSICUI,
  backend: {
    image: 'plone/plone-backend:6.1.3',
    replicas: 2,
    limitMemory: '1Gi',
    limitCpu: '1000m',
    environment: env,
  },
})

Varnish Caching

Edit config/varnish.tpl.vcl for caching rules specific to Classic UI.

Classic UI VCL is simpler than Volto’s - all traffic routes to one backend.

Scaling

Scale backend replicas:

backend: {
  replicas: 3,  // Increase for higher traffic
}

Then:

npm run synth
kubectl apply -f dist/plone-classic.k8s.yaml

Performance

Classic UI performance characteristics:

  • Server-side rendering can be slower than Volto’s client-side

  • Varnish caching is critical for performance

  • Database is the main bottleneck (use CloudNativePG for HA)

  • Fewer HTTP requests than Volto (no separate frontend API calls)

Cleanup

kubectl delete -f dist/plone-classic.k8s.yaml

Next Steps

See Also