Skip to content

m2p v3 Migration Guide

This guide documents breaking changes in m2p version 3 and how to migrate your mpy_meta_config from v2.

Breaking Changes

1. expose_publicly Replaced by public_exposure

The boolean expose_publicly field has been replaced by a Selection field public_exposure that specifies how the route is exposed publicly.

v2 (deprecated):

parts:
  main:
    expose_publicly: true  # Boolean only
    routes: [...]

v3 (required):

parts:
  main:
    public_exposure: mpy_traefik  # Selection field
    routes: [...]

2. public_exposure Values

Value Description TLS Strategy
(empty/omit) Private route only (default) Let's Encrypt via private domain
mpy_traefik Public via cluster's Traefik Let's Encrypt via public domain
proxied Public via external proxy (Cloudflare, etc.) Self-signed cert (proxy terminates TLS)

3. Migration Table

v2 Config v3 Equivalent
expose_publicly: true public_exposure: mpy_traefik
expose_publicly: false (remove line or omit field)
(field absent) (no change needed)

4. Debug Config Changes

The same change applies to debug_config:

v2:

debug_config:
  expose_publicly: true
  debug_fqdn_prefix: "codr-"

v3:

debug_config:
  public_exposure: mpy_traefik  # or 'proxied'
  debug_fqdn_prefix: "codr-"

When to Use Each Option

public_exposure: mpy_traefik

  • Direct public access via cluster's Traefik load balancer
  • Automatic Let's Encrypt certificate for public domain
  • DNS record points directly to cluster IP
  • Use case: Production apps exposed to internet with full SSL/TLS termination at Traefik

public_exposure: proxied

  • Public access via external proxy (Cloudflare, CDN, etc.)
  • Self-signed certificate in cluster (proxy terminates TLS)
  • DNS record points to proxy, proxy forwards to cluster
  • Use case: Apps behind CDN, WAF, or DDoS protection

No public_exposure (private only)

  • Only accessible via private DNS domain
  • Let's Encrypt certificate for private domain
  • Use case: Internal services, dev/staging environments

Complete Example

mpy_meta_config:
  parts:
    # Public API with direct Traefik access
    api:
      type: server
      public_exposure: mpy_traefik
      routes:
        - name: main
          pathPrefix: PathPrefix(`/`)
          port_name: http
      deployment:
        docker_image: "myapp:v1"
      ports:
        - name: http
          port: 8080

    # Public frontend behind Cloudflare
    frontend:
      type: server
      public_exposure: proxied
      routes:
        - name: main
          pathPrefix: PathPrefix(`/`)
          port_name: http
      deployment:
        docker_image: "myapp-frontend:v1"
      ports:
        - name: http
          port: 3000

    # Internal admin panel (no public exposure)
    admin:
      type: server
      # No public_exposure = private only
      routes:
        - name: main
          pathPrefix: PathPrefix(`/`)
          port_name: http
          middlewares:
            ipwhitelist: true
      deployment:
        docker_image: "myapp-admin:v1"
      ports:
        - name: http
          port: 8080

Prerequisites for Public Exposure

For public_exposure to work, the following must be configured on the Release:

  1. Public DNS Domain (public_dns_domain_id)
  2. Public Hostname Generator (public_hostname_generator_id)

These are typically inherited from the Package Profile or set manually on the Release.

TLS Certificate Management

Private Routes (no public_exposure)

  • Uses letsencrypt-issuer (existing behavior)
  • Certificate stored in {release_key}-{part_name}-cert Secret

mpy_traefik Routes

  • Uses new letsencrypt-issuer-public issuer
  • Certificate stored in {release_key}-{part_name}-public-cert Secret
  • Requires tls.public_dns_domain configuration in values

proxied Routes

  • Uses Traefik's auto-generated self-signed certificate (tls: {})
  • External proxy (Cloudflare, etc.) handles public TLS termination

Sunray Zero Trust Authentication

Sunray is a Zero Trust authentication system using Traefik's ForwardAuth middleware. It intercepts HTTP requests and validates them through a Sunray Worker service before forwarding to your application.

Prerequisites

For Sunray to work, the following must be configured:

  1. Cluster Level: default_sunray_worker_release_id pointing to a deployed Sunray Worker
  2. Release Level: Inherits from cluster automatically, or configure sunray_worker_release_id manually

Verify Sunray is available by checking that sunray.available: true appears in your rendered values.

enable_sunray Configuration

Add enable_sunray at the part level (not inside routes or middlewares):

Value Private Route Public Route Use Case
all Enabled Enabled Full protection on all routes
public Disabled Enabled Protect only public-facing routes
(omit) Disabled Disabled No Sunray protection

Part-Level Example

mpy_meta_config:
  parts:
    gui:
      type: server
      enable_sunray: public         # Sunray on public routes only
      public_exposure: mpy_traefik  # Required for public routes
      routes:
        - name: main
          pathPrefix: PathPrefix(`/`)
          port_name: http
          middlewares:
            ipwhitelist: true       # Can combine with other middlewares
      # ...

Debug Config with Sunray

Protect your code-server debug endpoint with Sunray:

debug_mode_available: codr
debug_config:
  debug_fqdn_prefix: codr-
  enable_sunray: all              # Protect debug access
  public_exposure: mpy_traefik    # Optional: expose debug publicly
  command: [/usr/bin/code-server]
  args: [--bind-addr=0.0.0.0:8765, --auth=password]

Complete Example (Odoo 18 with Sunray)

mpy_meta_config:
  parts:
    gui:
      type: server
      dashboard_name: Odoo GUI / API
      enable_sunray: public           # Sunray on public routes
      public_exposure: mpy_traefik
      deployment:
        command: [ 'bin/odoo-ikb' ]
        args:
        - --max-cron-threads=0
        - --workers=4
      ports:
      - name: odoo
        port: 8069
      - name: gevent
        port: 8072
      - name: codr
        port: 8765
        debug_mode: true
      routes:
      - name: gui
        pathPrefix: PathPrefix(`/`)
        port_name: odoo
        middlewares:
          ipwhitelist: true
      - name: longpolling
        pathPrefix: PathPrefix(`/websocket`) || PathPrefix(`/longpolling`)
        port_name: gevent
        middlewares:
          ipwhitelist: true
      debug_mode_available: codr
      debug_config:
        debug_fqdn_prefix: codr-
        enable_sunray: all            # Full Sunray on debug
        command: [/usr/bin/code-server]
        args: [--bind-addr=0.0.0.0:8765, --auth=password]

After Modifying Parts Config

  1. Click "Sync 'Parts Config.'" on the Installed Package
  2. Network Routes will be updated with enable_middleware_sunray flags
  3. Optionally adjust the toggle manually in the Network tab

Important Notes

  • The middlewares.sunray: true syntax inside routes is NOT supported
  • Only use enable_sunray at the part level or inside debug_config
  • Sunray middleware is only applied when both:
  • enable_sunray is configured in Parts Config
  • sunray.available: true in the cluster configuration

Validation

m2p v3 will fail with a clear error if the chart version requires v3 features:

Error: m2p v3+ required. This chart requires network_routes and sunray configuration.

Update your mpy_meta_config to use public_exposure instead of expose_publicly.

Troubleshooting

Certificate not generated for public route

  1. Verify public_exposure: mpy_traefik is set (not proxied)
  2. Check tls.public_dns_domain.certmanager_solver_name is configured in values
  3. Verify letsencrypt-issuer-public Issuer exists: kubectl get issuer -n <namespace>

DNS record not created

  1. Verify manage_public_dns_records is enabled on the Release
  2. Check public_dns_domain_id and public_hostname_generator_id are set
  3. Run "Setup DNS Records" button to create records

Route not accessible publicly

  1. Verify LoadBalancer has external IP: kubectl get svc -n <namespace>
  2. Check IngressRoute exists: kubectl get ingressroute -n <namespace>
  3. Verify DNS resolution points to correct IP