Summary: nuxt-website is one codebase that produces two completely separate Docker images, one per brand, via the THEME=7mind or THEME=7sleep build-time environment variable. Unlike the backend’s request-time dual-brand-routing, the frontend bakes the brand in at build time and deploys two distinct containers. Sources: direct code inspection (Dockerfile, nuxt.config.ts, theme-helpers.js, themes.json, .github/workflows/_build-and-deploy.yml, .github/workflows/deploy-from-merge-legacy.yml) Last updated: 2026-05-15


Why build-time, not request-time

The Nuxt website cannot do what the elixir-backend does (one binary, two brands chosen per request). The reasons are concrete:

  • SCSS variables, design tokens, and Google Fonts differ between brands. Sass and style-dictionary resolve these at build time.
  • Locale lists differ massively: 7mind builds nine locales (de, fr, nl, en, it, es, uk, us, ca) on different TLDs; 7sleep builds only de. nuxt-i18n bakes this into the route table.
  • Auth is enabled for 7mind only. auth: false on 7sleep changes the generated routes.
  • Sitemap shape, recaptcha keys, Sentry DSN, favicons, even the cookie banner copy are all brand-specific and resolved during build.

So the answer is two separate builds. The brand decision is made once, in the Docker build args, and burned into the artifact.

How the multiplexing works

  1. Dockerfile accepts ARG THEME and sets ENV THEME=$THEME.
  2. CI passes THEME=7mind (or 7sleep) as a build arg. The current new pipeline (_build-and-deploy.yml) only builds THEME=7mind. The legacy pipeline (deploy-from-merge-legacy.yml) builds both as separate parallel jobs producing two images: web-v2 and web-sleep.
  3. nuxt.config.ts reads process.env.THEME at config-load time and branches every brand-sensitive setting from there.
  4. theme-helpers.js + themes.json map a theme name to its SCSS variables path (assets/scss/7mind vs assets/scss/7sleep).
  5. The webpack extend hook in nuxt.config.ts registers a ~brandVariables alias and a SCSS rule chain that uses null-loader for files containing other-theme queries — so other-brand SCSS is dropped during compile.
  6. Design tokens live under tokens/{7mind,7sleep}/ and are compiled into Vue-accessible JS at build time by modules/design-tokens.js.
  7. Brand-specific components live under components/7sleep/ or are gated via <template> conditionals on $config.theme.

Which brand goes where

ImageThemeDeploy targetPipeline
web-v2 (new)7mindGKE application-cluster via Terraform, namespaces staging / production_build-and-deploy.yml
web-v2 (legacy)7mindOld GKE cluster via Helmdeploy-from-merge-legacy.yml
web-sleep (legacy)7sleepOld GKE cluster via Helmdeploy-from-merge-legacy.yml

The new Terraform pipeline does not currently build a 7sleep image. As of the latest workflow files, THEME=7mind is hardcoded. The 7sleep production frontend is still served from the legacy pipeline. Migrating 7sleep onto the new infra requires either a second Terraform module instance with its own image and ingress or a build matrix in _build-and-deploy.yml.

What agents should know

  • Never hardcode brand in templates. Always use $config.theme === '7mind' (or read process.env.THEME at build time) and branch accordingly.
  • process.env.THEME is set at build time, not runtime. Changing it in K8s ConfigMap after the build does nothing; the SCSS, locales, and tokens are already compiled.
  • Adding a brand-specific feature means it can be present in one bundle and absent from the other, not toggleable at runtime. Plan accordingly.
  • The new Terraform pipeline is 7mind-only. Don’t assume /deploy production ships 7sleep changes; it does not. 7sleep deploys come from the legacy deploy-from-merge-legacy.yml.
  • Sentry projects, GTM containers, Chargebee namespaces, Braze keys, and reCAPTCHA keys all come in pairs keyed on THEME. When adding integrations, add both unless you have a documented reason to only support one brand.
  • SCSS that uses other-brand variables fails the build because of the ~brandVariables alias resolution. If you need to share SCSS, put it in shared paths that don’t reference theme-specific tokens.
  • The legacy pipeline still uses Helm under k8s/base/{web-v2,web-sleep}/ and pushes images to eu.gcr.io/mind-f62c0. The new pipeline uses Terraform under terraform/web-v2/ and pushes to europe-west3-docker.pkg.dev/sevenmind-infrastructure/application-cluster-repo/web-v2. Both register routes to the same public domains; do not assume only one ingress exists in front of www.7mind.de.