Docs
On this page

Build and deploy a Next.js app

This tutorial takes a small Next.js application from an empty Sandbox to a verified production URL.

You will build in a Sandbox, inspect a temporary Preview, publish an immutable Version, verify the stable Deployment URL, and optionally connect app.example.com.

What you will create

ResourceExamplePurpose
Sandboxhello-nextjsMutable place to create and test the application.
PreviewAPI-returned temporary URLReview the live development server before publishing.
Deploymenthello-nextjsStable production identity and version history.
Platform URLAPI-returned public_urlImmediate production access without customer DNS.
Custom domainapp.example.comOptional customer-owned hostname attached to the Deployment.

Before you start

You need:

  • A MIOSA User with access to the intended Organization and Workspace.
  • A workspace API key stored only in a server or terminal environment.
  • The MIOSA CLI installed.
  • Permission to create Sandboxes, publish Deployments, and configure domains.
  • Access to the DNS provider only if you complete the custom-domain section.
export MIOSA_API_KEY="msk_u_..."
miosa whoami

Confirm that the response identifies the expected Organization and Workspace before creating anything.

Step 1: Create the Sandbox

miosa sandbox create 
  --name hello-nextjs 
  --template node 
  --timeout 1h 
  --wait 
  --json

Save the returned Sandbox ID:

export SANDBOX_ID="<sandbox-id>"

Expected result: the Sandbox reaches a usable running state.

If creation remains pending, inspect the returned capacity or readiness reason before retrying. Do not create repeated Sandboxes while the first request is still progressing.

Step 2: Create the Next.js application

Open the Sandbox terminal from the MIOSA interface, then run:

cd /workspace
pnpm create next-app@latest hello-nextjs 
  --typescript 
  --eslint 
  --app 
  --no-tailwind 
  --use-pnpm 
  --yes
cd /workspace/hello-nextjs

Replace app/page.tsx with:

export default function Home() {
  return (
    <main
      style={{
        minHeight: "100vh",
        display: "grid",
        placeItems: "center",
        background: "#0a0a0a",
        color: "#fafafa",
        fontFamily: "system-ui, sans-serif",
      }}
    >
      <section style={{ textAlign: "center" }}>
        <p style={{ color: "#a1a1aa" }}>Deployed with MIOSA</p>
        <h1 style={{ fontSize: "clamp(3rem, 10vw, 7rem)", margin: 0 }}>
          Hello from MIOSA
        </h1>
      </section>
    </main>
  )
}

Run the production build before creating a Preview:

pnpm build

Expected result: the command exits with status 0 and Next.js reports a successful build.

Step 3: Start and preview the application

Start the application inside the Sandbox:

pnpm start --hostname 0.0.0.0 --port 3000

Keep that process running, then create a Preview from your local terminal:

miosa sandbox preview "$SANDBOX_ID" 
  --port 3000 
  --wait 
  --json

Open the exact Preview URL returned by MIOSA.

Expected result: the page displays “Hello from MIOSA.”

Step 4: Publish through App Engine

Ensure the Workspace has an active App Engine Host:

miosa docker-deploy ensure --wait --timeout 600 --json

Publish the Next.js application:

miosa sandbox publish "$SANDBOX_ID" 
  --name hello-nextjs 
  --slug hello-nextjs 
  --path /workspace/hello-nextjs 
  --run-command "pnpm start --hostname 0.0.0.0 --port 3000" 
  --port 3000 
  --docker-deploy 
  --wait 
  --json

Save these values from the response:

export DEPLOYMENT_ID="<deployment-id>"
export PUBLIC_URL="<deployment-public-url>"

Always use the returned public_url. Do not construct a production hostname from the slug, Organization name, or Workspace name.

Expected result: the response identifies the Deployment, candidate Release, promoted Version, operation evidence, and public_url.

Step 5: Prove production

A successful build is not enough. Prove that MIOSA can resolve the Deployment, App Engine Host, application target, running route, and public response.

miosa deploy prove "$DEPLOYMENT_ID" --json

Then verify the public response independently:

curl --fail --silent --show-error "$PUBLIC_URL" | grep "Hello from MIOSA"

Expected result: deployment proof passes and the public response contains “Hello from MIOSA.”

Step 6: Connect a custom domain

This step is optional. The API-selected platform URL remains valid while DNS is being configured.

In the Deployment’s domain settings, enter the exact hostname you want, such as:

app.example.com

MIOSA displays the required DNS record and verification target. Copy those values exactly into the DNS provider.

For a normal subdomain, the record generally has this shape:

TypeHostValueProxying
CNAMEappThe target shown by MIOSADisabled until verification completes

Do not copy example.com literally. Use a hostname controlled by the customer and the target displayed in the live MIOSA domain flow.

Check public DNS:

dig app.example.com CNAME +short

Return to MIOSA and select Verify DNS.

Expected result: the domain moves through DNS verification, certificate issuance, and routing until HTTPS is active.

Step 7: Publish an update safely

Change the heading in app/page.tsx, rebuild in the Sandbox, and publish to the existing Deployment:

miosa sandbox publish "$SANDBOX_ID" 
  --app "$DEPLOYMENT_ID" 
  --path /workspace/hello-nextjs 
  --run-command "pnpm start --hostname 0.0.0.0 --port 3000" 
  --port 3000 
  --docker-deploy 
  --wait 
  --json

Using --app preserves the Deployment identity, stable URL, domains, bindings, and release history.

Run deployment proof again after the new Version is promoted.

Step 8: Understand rollback before you need it

List the Deployment’s releases:

miosa releases list "$DEPLOYMENT_ID" --json

If the new Version is unhealthy, promote the earlier ready Release:

miosa releases rollback <release-id> 
  --app "$DEPLOYMENT_ID" 
  --yes 
  --json

Rollback changes the active Version. It does not change the Deployment identity or require a new domain.

Common failures and recovery

SymptomLikely causeSafe actionProof after repair
Sandbox creation remains pendingRequested capacity is not ready or the request is still progressing.Inspect the readiness reason and wait or select compatible capacity.The original Sandbox reaches a usable state.
Preview returns an errorThe Next.js process is stopped, bound to the wrong interface, or using another port.Start it on 0.0.0.0:3000 and recreate or refresh the Preview.The Preview returns “Hello from MIOSA.”
Publish reports App Engine is unavailableThe Workspace App Engine Host is not active.Run miosa docker-deploy ensure --wait --timeout 600 --json, then retry the same publish.Publish returns a Deployment and public_url.
Build succeeds but the application is not livePromotion, container health, or routing proof has not completed.Inspect the publish result and run miosa deploy prove.Proof passes and the public URL responds.
DNS remains pendingThe record has not propagated, uses the wrong host, or points to the wrong target.Compare public DNS with the exact record shown by MIOSA and keep proxying disabled during verification.MIOSA reports DNS verified and TLS active.
The custom hostname opens another applicationThe hostname was attached in the wrong Organization or to the wrong Deployment.Stop and inspect ownership and route bindings before changing DNS again.The domain resolves to the intended Deployment ID and active Version.
A new publish causes a regressionThe candidate passed build but application behavior is incorrect.Roll back to the earlier ready Release.The stable URL serves the previous known-good content.

Completion checklist

  • The Sandbox exists in the intended Organization and Workspace.
  • The Next.js production build exits successfully.
  • The Preview displays “Hello from MIOSA.”
  • Publishing returns a Deployment ID and API-selected public_url.
  • miosa deploy prove passes.
  • The public URL returns the expected content.
  • The custom domain is verified and TLS-active, if configured.
  • An earlier ready Release is identifiable for rollback.
  • The Sandbox can be removed without deleting the Deployment.

Continue from here

Was this helpful?