diff --git a/stack_orchestrator/build/build_webapp_snowball.py b/stack_orchestrator/build/build_webapp_snowball.py
new file mode 100644
index 00000000..8719c826
--- /dev/null
+++ b/stack_orchestrator/build/build_webapp_snowball.py
@@ -0,0 +1,117 @@
+# Copyright © 2022, 2023 Vulcanize
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+# Builds webapp containers
+
+# env vars:
+# CERC_REPO_BASE_DIR defaults to ~/cerc
+
+# TODO: display the available list of containers; allow re-build of either all or specific containers
+
+import os
+import sys
+
+from decouple import config
+import click
+from pathlib import Path
+from stack_orchestrator.build import build_containers
+from stack_orchestrator.deploy.webapp.util import determine_base_container, TimedLogger
+from stack_orchestrator.build.build_types import BuildContext
+
+
+@click.command()
+@click.option('--base-container')
+@click.option('--source-repo', help="directory containing the webapp to build", required=True)
+@click.option("--force-rebuild", is_flag=True, default=False, help="Override dependency checking -- always rebuild")
+@click.option("--extra-build-args", help="Supply extra arguments to build")
+@click.option("--tag", help="Container tag (default: cerc/:local)")
+@click.pass_context
+def command(ctx, base_container, source_repo, force_rebuild, extra_build_args, tag):
+ '''build the specified webapp container'''
+ logger = TimedLogger()
+
+ quiet = ctx.obj.quiet
+ debug = ctx.obj.debug
+ verbose = ctx.obj.verbose
+ local_stack = ctx.obj.local_stack
+ stack = ctx.obj.stack
+
+ # See: https://stackoverflow.com/questions/25389095/python-get-path-of-root-project-structure
+ container_build_dir = Path(__file__).absolute().parent.parent.joinpath("data", "container-build")
+
+ if local_stack:
+ dev_root_path = os.getcwd()[0:os.getcwd().rindex("stack-orchestrator")]
+ logger.log(f'Local stack dev_root_path (CERC_REPO_BASE_DIR) overridden to: {dev_root_path}')
+ else:
+ dev_root_path = os.path.expanduser(config("CERC_REPO_BASE_DIR", default="~/cerc"))
+
+ if verbose:
+ logger.log(f'Dev Root is: {dev_root_path}')
+
+ if not base_container:
+ base_container = determine_base_container(source_repo)
+
+ # First build the base container.
+ container_build_env = build_containers.make_container_build_env(dev_root_path, container_build_dir, debug,
+ force_rebuild, extra_build_args)
+
+ if verbose:
+ logger.log(f"Building base container: {base_container}")
+
+ build_context_1 = BuildContext(
+ stack,
+ base_container,
+ container_build_dir,
+ container_build_env,
+ dev_root_path,
+ )
+ ok = build_containers.process_container(build_context_1)
+ if not ok:
+ logger.log("ERROR: Build failed.")
+ sys.exit(1)
+
+ if verbose:
+ logger.log(f"Base container {base_container} build finished.")
+
+ # Now build the target webapp. We use the same build script, but with a different Dockerfile and work dir.
+ container_build_env["CERC_WEBAPP_BUILD_RUNNING"] = "true"
+ container_build_env["CERC_CONTAINER_BUILD_WORK_DIR"] = os.path.abspath(source_repo)
+ container_build_env["CERC_CONTAINER_BUILD_DOCKERFILE"] = os.path.join(container_build_dir,
+ base_container.replace("/", "-"),
+ "Dockerfile")
+ if not tag:
+ webapp_name = os.path.abspath(source_repo).split(os.path.sep)[-1]
+ tag = f"cerc/{webapp_name}:local"
+
+ container_build_env["CERC_CONTAINER_BUILD_TAG"] = tag
+
+ if verbose:
+ logger.log(f"Building app container: {tag}")
+
+ build_context_2 = BuildContext(
+ stack,
+ base_container,
+ container_build_dir,
+ container_build_env,
+ dev_root_path,
+ )
+ ok = build_containers.process_container(build_context_2)
+ if not ok:
+ logger.log("ERROR: Build failed.")
+ sys.exit(1)
+
+ if verbose:
+ logger.log(f"App container {base_container} build finished.")
+ logger.log("build-webapp-snowball complete", show_step_time=False, show_total_time=True)
diff --git a/stack_orchestrator/data/container-build/cerc-nextjs-snowball/Dockerfile b/stack_orchestrator/data/container-build/cerc-nextjs-snowball/Dockerfile
new file mode 100644
index 00000000..07855c4e
--- /dev/null
+++ b/stack_orchestrator/data/container-build/cerc-nextjs-snowball/Dockerfile
@@ -0,0 +1,52 @@
+FROM node:18-alpine AS base
+
+# 1. Install dependencies only when needed
+FROM base AS deps
+# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
+RUN apk add --no-cache libc6-compat
+
+WORKDIR /app
+
+# Install dependencies based on the preferred package manager
+COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
+RUN \
+ if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
+ elif [ -f package-lock.json ]; then npm ci; \
+ elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i; \
+ else echo "Lockfile not found." && exit 1; \
+ fi
+
+
+# 2. Rebuild the source code only when needed
+FROM base AS builder
+WORKDIR /app
+COPY --from=deps /app/node_modules ./node_modules
+COPY . .
+# This will do the trick, use the corresponding env file for each environment.
+COPY .env.production.sample .env.production
+RUN npm run build
+
+# 3. Production image, copy all the files and run next
+FROM base AS runner
+WORKDIR /app
+
+ENV NODE_ENV=production
+
+RUN addgroup -g 1001 -S nodejs
+RUN adduser -S nextjs -u 1001
+
+COPY --from=builder /app/public ./public
+
+# Automatically leverage output traces to reduce image size
+# https://nextjs.org/docs/advanced-features/output-file-tracing
+COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
+COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
+
+USER nextjs
+
+# Expose port for http
+EXPOSE 80
+
+ENV PORT=80
+
+CMD HOSTNAME="0.0.0.0" node server.js
diff --git a/stack_orchestrator/data/container-build/cerc-nextjs-snowball/build.sh b/stack_orchestrator/data/container-build/cerc-nextjs-snowball/build.sh
new file mode 100755
index 00000000..8d4e26ae
--- /dev/null
+++ b/stack_orchestrator/data/container-build/cerc-nextjs-snowball/build.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+# Build cerc/cerc-nextjs-snowball
+
+source ${CERC_CONTAINER_BASE_DIR}/build-base.sh
+
+# See: https://stackoverflow.com/a/246128/1701505
+SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+
+CERC_CONTAINER_BUILD_WORK_DIR=${CERC_CONTAINER_BUILD_WORK_DIR:-$SCRIPT_DIR}
+CERC_CONTAINER_BUILD_DOCKERFILE=${CERC_CONTAINER_BUILD_DOCKERFILE:-$SCRIPT_DIR/Dockerfile}
+CERC_CONTAINER_BUILD_TAG=${CERC_CONTAINER_BUILD_TAG:-cerc/nextjs-snowball:local}
+
+CERC_NPM_REGISTRY_URL="https://git.vdb.to/api/packages/cerc-io/npm/"
+
+docker build -t cerc/nextjs-snowball:local ${build_command_args} -f ${SCRIPT_DIR}/Dockerfile ${CERC_REPO_BASE_DIR}/nextjs-snowball
diff --git a/stack_orchestrator/deploy/webapp/util.py b/stack_orchestrator/deploy/webapp/util.py
index c6b6a786..b99ab652 100644
--- a/stack_orchestrator/deploy/webapp/util.py
+++ b/stack_orchestrator/deploy/webapp/util.py
@@ -482,6 +482,7 @@ def determine_base_container(clone_dir, app_type="webapp"):
base_container = "cerc/webapp-base"
if app_type == "webapp/next":
+ # base_container = "cerc/nextjs-snowball"
base_container = "cerc/nextjs-base"
elif app_type == "webapp":
pkg_json_path = os.path.join(clone_dir, "package.json")
diff --git a/stack_orchestrator/main.py b/stack_orchestrator/main.py
index 5ae10468..b9ff86a7 100644
--- a/stack_orchestrator/main.py
+++ b/stack_orchestrator/main.py
@@ -21,6 +21,7 @@ from stack_orchestrator.repos import fetch_stack
from stack_orchestrator.build import build_containers, fetch_containers
from stack_orchestrator.build import build_npms
from stack_orchestrator.build import build_webapp
+from stack_orchestrator.build import build_webapp_snowball
from stack_orchestrator.deploy.webapp import (run_webapp,
deploy_webapp,
deploy_webapp_from_registry,
@@ -59,6 +60,7 @@ cli.add_command(build_containers.command, "build-containers")
cli.add_command(fetch_containers.command, "fetch-containers")
cli.add_command(build_npms.command, "build-npms")
cli.add_command(build_webapp.command, "build-webapp")
+cli.add_command(build_webapp_snowball.command, "build-webapp-snowball")
cli.add_command(run_webapp.command, "run-webapp")
cli.add_command(deploy_webapp.command, "deploy-webapp")
cli.add_command(deploy_webapp_from_registry.command, "deploy-webapp-from-registry")
@@ -69,4 +71,4 @@ cli.add_command(deploy.command, "deploy") # deploy is an alias for deploy-syste
cli.add_command(deploy.command, "deploy-system")
cli.add_command(deployment.command, "deployment")
cli.add_command(version.command, "version")
-cli.add_command(update.command, "update")
+cli.add_command(update.command, "update")
\ No newline at end of file