Deploying

Bascik's build output is a standard folder of static HTML, CSS, and JavaScript files. bascik --build writes everything to dist/, and that folder can be served by any static host or CDN without additional configuration.

What's in dist/

Running bascik --build produces:

  • HTML: compiled pages with component tags resolved, scoped class names applied, and build-script output inlined
  • CSS and JS: as-is from your src/pages/ asset folders
  • Static assets: images, fonts, and any other files copied unchanged

The output uses root-relative paths (e.g. /css/styles.css). Files must be served from an HTTP server; opening them directly with file:// will break asset loading.

Previewing static builds locally

To preview your built site locally before deploying, run Bascik's built-in production server:

sh
bascik --serve

Or preview with any third-party static HTTP server:

sh
npx http-server dist

Then open http://localhost:8080 in your browser to inspect your production site.

Static hosting

For most Bascik sites, dist/ is the deployable artifact. If your site has no data-bascik-server scripts, you only need a static host.

Every major platform follows the same pattern:

  1. Run bascik --build to produce dist/
  2. Configure the host to deploy from the dist/ folder
  3. Point the publish directory at dist/

That covers GitHub Pages, Netlify, Cloudflare Pages, AWS S3, Vercel, and any other static host. Refer to your hosting provider's documentation for the exact steps. Because the output is vanilla HTML, CSS, and JS, it follows the same conventions as Vite, Astro, and other tools, so guides for those tools are largely applicable.

Tips that apply everywhere

Custom 404 page. Name your page src/pages/404.html. After building, dist/404.html is the standard location for custom 404 pages recognized by GitHub Pages, Netlify, Cloudflare Pages, and Vercel.

Root-relative paths. The build output uses root-relative paths. Serve the site at the domain root, or configure your host's base path setting to match.

Build command. If your host runs a build command for you, use npx bascik --build or bascik --build (if installed as a dev dependency). Set the output directory to dist/.

No runtime required. Bascik does not need Node.js at serve time for static sites. Any CDN or file server that can serve HTML files is sufficient.

GitHub Actions example

A minimal workflow for building and uploading to any static host:

yaml
name: Build
on:
  push:
    branches: [main]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v5
        with:
          node-version: '24'
      - run: npm ci
      - run: npx bascik --build
      # Upload dist/ to your host here

dist/ is the artifact to upload or deploy.

Using the production server

If your site uses data-bascik-server scripts for per-request dynamic content, you need infrastructure that can execute Node.js alongside the built files. The built-in production server handles this without any additional framework.

sh
bascik --build   # compile to dist/
bascik --serve   # start the HTTP server; runs server scripts per request

See Production Server for full documentation on server scripts and the request context API.

Plaintext HTTP by default. Bascik's server runs as a plaintext HTTP/1.1 server by default on port 8080. Most cloud platforms (Heroku, Fly.io, AWS ECS, Render, etc.) terminate TLS at the edge and forward cleartext HTTP to your container. If your platform passes TLS through to the container or you want HTTP/2, set enableTls: true under prodServer in your bascik.config.ts.

Server configuration

Configure the port and TLS in bascik.config.ts before building:

ts
export default {
  prodServer: {
    port: 8080,
    hostname: '0.0.0.0',    // bind all interfaces; required in containers
    enableTls: false,       // set to true for TLS-enabled HTTP/2
  },
};

When keyFile and certFile are omitted, Bascik generates certificates automatically using mkcert (if installed) or openssl as a fallback. For production, supply a certificate signed by a trusted CA.

Containers

A two-stage Dockerfile keeps the final image lean:

dockerfile
# Stage 1 — build
FROM node:24-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npx bascik --build

# Stage 2 — serve
FROM node:24-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY --from=build /app/dist ./dist
COPY bascik.config.ts .
EXPOSE 8080
CMD ["npx", "bascik", "--serve"]

If enableTls: true is set, you can supply a real certificate by mounting it at runtime:

sh
docker run -p 8443:8443 \
  -v /etc/letsencrypt/live/example.com/privkey.pem:/app/bascik-privkey.pem:ro \
  -v /etc/letsencrypt/live/example.com/fullchain.pem:/app/bascik-cert.pem:ro \
  my-site

Bascik looks for bascik-privkey.pem and bascik-cert.pem in the working directory by default; mounting them at those paths means no config change is needed.

PaaS (Railway, Render, Fly.io, etc.)

Set the start command to bascik --build && bascik --serve, point the platform's health check at the server port, and configure the port via the serve.port setting to match the port the platform expects to expose.

VPS or bare metal

Obtain a certificate (e.g. from Let's Encrypt via certbot) and point Bascik at it in bascik.config.ts. Then create a systemd unit to keep the server running:

ini
# /etc/systemd/system/my-site.service
[Unit]
Description=My Bascik Site
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/srv/my-site
ExecStartPre=/usr/bin/npx bascik --build
ExecStart=/usr/bin/npx bascik --serve
Restart=on-failure
RestartSec=5s
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target
sh
systemctl daemon-reload
systemctl enable --now my-site
journalctl -u my-site -f

Behind a reverse proxy

If you already run nginx, Caddy, or another proxy, proxy HTTPS traffic to Bascik. The proxy-to-backend leg can use Bascik's self-signed certificate; only the client-facing edge needs a trusted cert. Pass the original client IP and any authentication headers through to data-bascik-server scripts so they have access to them.

Note that the built-in rate limiter reads the TCP remote address. When running behind a proxy, all requests arrive from the proxy's IP, so rate limiting should be configured at the proxy layer instead.

Platform docs. Consult your hosting provider's documentation for specifics around caching headers, redirects, environment variables, and build pipelines. Bascik's output is standard enough that most generic guides apply directly.