> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blaxel.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Set up Astro with Blaxel Sandboxes

> Run an Astro application in a Blaxel Sandbox and expose it securely with a Blaxel preview URL.

This guide explains how to run an Astro application inside a Blaxel Sandbox and expose it securely using Blaxel previews.

## Prerequisites

* [Blaxel CLI](../../cli-reference/introduction) installed and authenticated with `bl login`
* Node.js 18 or later
* `@blaxel/core` installed in your project with `npm install @blaxel/core`

## 1. Understand the architecture

Running Astro inside a Blaxel Sandbox requires a few adjustments:

* Astro normally binds to localhost
* Blaxel exposes services through preview URLs
* Astro must accept external connections and all hosts

Configure `astro.config.mjs` with `host: "0.0.0.0"` and `allowedHosts: true`, then expose the Astro development server through a [Blaxel preview URL](../../Sandboxes/Preview-url).

## 2. Build the sandbox image

Create a `Dockerfile`:

```dockerfile theme={null}
FROM oven/bun:alpine

RUN apk update && apk add --no-cache \
  git \
  curl \
  netcat-openbsd \
  nodejs \
  npm \
  && rm -rf /var/cache/apk/*

WORKDIR /app

COPY --from=ghcr.io/blaxel-ai/sandbox:latest /sandbox-api /usr/local/bin/sandbox-api

# Create the Astro project with npx, then use Bun for dependencies
RUN npx create-astro@latest /app --template basics --no-install --no-git --yes \
  && bun install

COPY ./astro.config.mjs /app/astro.config.mjs
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]
```

## 3. Configure Astro

Create `astro.config.mjs` to allow external connections:

```javascript theme={null}
// @ts-check
import { defineConfig } from "astro/config";

export default defineConfig({
  server: {
    host: "0.0.0.0",
    port: 4321,
    allowedHosts: true,
  },
});
```

## 4. Create the entrypoint

Create `entrypoint.sh` to start the sandbox API and Astro development server:

```bash theme={null}
#!/bin/sh

export PATH="/usr/local/bin:$PATH"

/usr/local/bin/sandbox-api &

wait_for_port() {
  local port=$1
  local timeout=30
  local count=0

  echo "Waiting for port $port to be available..."
  while ! nc -z localhost "$port"; do
    sleep 1
    count=$((count + 1))
    if [ "$count" -gt "$timeout" ]; then
      echo "Timeout waiting for port $port"
      exit 1
    fi
  done
  echo "Port $port is now available"
}

wait_for_port 8080

echo "Running Astro dev server..."
curl http://localhost:8080/process \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "name": "dev-server",
    "workingDir": "/app",
    "command": "bun run dev",
    "waitForCompletion": false,
    "restartOnFailure": true,
    "maxRestarts": 25
  }'

wait
```

## 5. Configure and deploy the image

Create `blaxel.toml` beside the `Dockerfile`:

```toml theme={null}
type = "sandbox"
name = "astro-template"

[runtime]
memory = 4096

[[runtime.ports]]
name = "astro-dev"
target = 4321
protocol = "tcp"
```

Deploy the image:

```bash theme={null}
bl deploy
```

## 6. Create or reuse a sandbox

```typescript theme={null}
import { SandboxInstance } from "@blaxel/core";

const sandboxName = "my-astro-sandbox";

const sandbox = await SandboxInstance.createIfNotExists({
  name: sandboxName,
  labels: {
    framework: "astro",
  },
  image: "astro-template:latest",
  memory: 4096,
  ports: [
    { name: "preview", target: 4321, protocol: "HTTP" },
  ],
});
```

## 7. Configure preview response headers

Astro development servers work well with permissive CORS headers when accessed through a preview URL:

```typescript theme={null}
const responseHeaders = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS, PATCH",
  "Access-Control-Allow-Headers":
    "Content-Type, Authorization, X-Requested-With, X-Blaxel-Workspace, X-Blaxel-Preview-Token, X-Blaxel-Authorization",
  "Access-Control-Allow-Credentials": "true",
  "Access-Control-Expose-Headers": "Content-Length, X-Request-Id",
  "Access-Control-Max-Age": "86400",
  Vary: "Origin",
};
```

Alternatively, use [custom domains](../../Infrastructure/Custom-domains) to expose previews on your own domain.

## 8. Create the preview and token

Astro runs on port `4321`. Create a private preview for that port:

```typescript theme={null}
const preview = await sandbox.previews.createIfNotExists({
  metadata: { name: "dev-server-preview" },
  spec: {
    responseHeaders,
    public: false,
    port: 4321,
  },
});
```

Generate a token that expires after one day:

```typescript theme={null}
const expiresAt = new Date(Date.now() + 1000 * 60 * 60 * 24);
const token = await preview.tokens.create(expiresAt);
```

## 9. Start and monitor the development server

If you do not use the entrypoint script, start Astro programmatically:

```typescript theme={null}
async function startDevServer(sandbox: SandboxInstance) {
  console.log("Starting Astro dev server...");
  await sandbox.process.exec({
    name: "dev-server",
    command: "bun run dev",
    workingDir: "/app",
    waitForPorts: [4321],
    restartOnFailure: true,
    maxRestarts: 25,
  });
}
```

Stream its logs:

```typescript theme={null}
const logStream = sandbox.process.streamLogs("dev-server", {
  onLog(log) {
    console.log(log);
  },
});

logStream.close();
```

## 10. Access the Astro application

Once the server is running, open:

```text theme={null}
${preview.spec?.url}?bl_preview_token=${token.value}
```

## 11. Run the complete example

```typescript theme={null}
import { SandboxInstance } from "@blaxel/core";

const sandboxName = "my-astro-sandbox";

const responseHeaders = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS, PATCH",
  "Access-Control-Allow-Headers":
    "Content-Type, Authorization, X-Requested-With, X-Blaxel-Workspace, X-Blaxel-Preview-Token, X-Blaxel-Authorization",
  "Access-Control-Allow-Credentials": "true",
  "Access-Control-Expose-Headers": "Content-Length, X-Request-Id",
  "Access-Control-Max-Age": "86400",
  Vary: "Origin",
};

async function startDevServer(sandbox: SandboxInstance) {
  await sandbox.process.exec({
    name: "dev-server",
    command: "bun run dev",
    workingDir: "/app",
    waitForPorts: [4321],
    restartOnFailure: true,
    maxRestarts: 25,
  });
}

async function main() {
  try {
    const sandbox = await SandboxInstance.createIfNotExists({
      name: sandboxName,
      labels: {
        framework: "astro",
      },
      image: "astro-template:latest",
      memory: 4096,
      ports: [
        { name: "preview", target: 4321, protocol: "HTTP" },
      ],
    });

    const preview = await sandbox.previews.createIfNotExists({
      metadata: { name: "preview" },
      spec: {
        responseHeaders,
        public: false,
        port: 4321,
      },
    });

    const expiresAt = new Date(Date.now() + 1000 * 60 * 60 * 24);
    const token = await preview.tokens.create(expiresAt);

    const processes = await sandbox.process.list();
    if (!processes.find((process) => process.name === "dev-server")) {
      await startDevServer(sandbox);
    }

    const webUrl = `${preview.spec?.url}?bl_preview_token=${token.value}`;
    console.log(`Astro Preview URL: ${webUrl}`);

    const logStream = sandbox.process.streamLogs("dev-server", {
      onLog(log) {
        console.log(log);
      },
    });

    process.on("SIGINT", () => {
      logStream.close();
      process.exit(0);
    });
  } catch (error) {
    console.error("Error:", error);
    process.exit(1);
  }
}

main();
```

This setup runs Astro fully inside a Blaxel Sandbox, securely exposes the development server, supports hot module replacement, and uses Bun for fast dependency installation and development server performance. It is suitable for static sites, blogs, documentation, preview environments, internal demos, and AI-powered coding workflows.

## Resources

<CardGroup cols={2}>
  <Card title="Real-time previews" href="../../Sandboxes/Preview-url">
    Configure public and private preview URLs for sandbox services.
  </Card>

  <Card title="Sandbox images" href="../../Sandboxes/Templates">
    Build and manage reusable Blaxel Sandbox images.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.