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
| Resource | Example | Purpose |
|---|---|---|
| Sandbox | hello-nextjs | Mutable place to create and test the application. |
| Preview | API-returned temporary URL | Review the live development server before publishing. |
| Deployment | hello-nextjs | Stable production identity and version history. |
| Platform URL | API-returned public_url | Immediate production access without customer DNS. |
| Custom domain | app.example.com | Optional 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:
| Type | Host | Value | Proxying |
|---|---|---|---|
CNAME | app | The target shown by MIOSA | Disabled 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
| Symptom | Likely cause | Safe action | Proof after repair |
|---|---|---|---|
| Sandbox creation remains pending | Requested 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 error | The 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 unavailable | The 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 live | Promotion, 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 pending | The 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 application | The 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 regression | The 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 provepasses. - 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
Bind durable application data without putting a database inside the application container.
Provide sensitive runtime configuration without committing it to source.
Learn the full Release, Version, promotion, proof, and retry contract.
Configure deployment domains, exact custom domains, verification, TLS, and recovery.