The best way to deploy an HTML website and API to the cloud
Published: August 10, 2026
Category: Deployments · Fundamentals
Reading time: 6 minutes
Author: NEXUS AI Team
Short answer: if your HTML and your API are small enough to ship together, put them in one server (a static folder plus a few routes) and deploy that as a single container. It is one URL, no CORS, no second bill, and it fits on a free tier. Split them into two separately-hosted services only when you actually need independent scaling or a different tech stack for each side. This post covers both paths, with the commands for each.
The three ways people actually do this
1. One server, two jobs. An Express, FastAPI, or Flask app that serves your index.html (and any CSS/JS) as static files from one route, and your API as JSON routes on the same port. One deploy, one URL, one process. This is the right default for a portfolio site, an internal tool, a small SaaS MVP, or anything where the frontend and backend ship together.
2. Two hosts, one app. The HTML lives on a static host (a CDN edge network), the API lives on a separate compute host, and the frontend calls the API's URL over HTTPS with CORS enabled. This is the classic Vercel/Netlify-plus-Render/Railway pattern. It buys you independent scaling and a CDN for the static assets, at the cost of two dashboards, two bills, and a CORS config to maintain.
3. Raw cloud primitives. S3 or Cloud Storage plus a CDN in front for the HTML, then Lambda/Cloud Functions or a VM for the API. Full control, the most setup work, and you own the plumbing (TLS certs, IAM, deploy scripts) yourself.
Most people asking "what's the best way to deploy my HTML site and API" have something closer to option 1 in mind and don't need the operational overhead of options 2 or 3 yet.
| Setup time | Cost to start | Scaling | Best for | |
|---|---|---|---|---|
| One server, two jobs | ~5 minutes | Free tier fits | Vertical, then horizontal | MVPs, internal tools, small apps |
| Two hosts, one app | ~15–30 minutes | Two free tiers, or one paid | Independent per side | Apps that outgrew #1 |
| Raw cloud primitives | Hours to days | Pay-as-you-go | Fully manual | Teams with existing cloud infra |
Option 1: deploy HTML + API together as one container
This is the fastest path and the one this section walks through end to end using NEXUS AI, which auto-detects a static folder next to an API server and builds a single production Dockerfile for it — no Dockerfile required on your side.
Project layout
my-app/
public/
index.html
style.css
app.js
server.js ← serves /public and the /api routes
package.json
// server.js
const express = require("express");
const path = require("path");
const app = express();
app.use(express.static(path.join(__dirname, "public")));
app.use(express.json());
app.get("/api/health", (req, res) => res.json({ status: "ok" }));
app.get("/api/items", (req, res) => {
res.json([{ id: 1, name: "First item" }]);
});
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on ${port}`));
Deploy it
Install the CLI and log in:
curl -fsSL https://nexusai.run/install.sh | bash # Linux
curl -fsSL https://nexusai.run/install-mac.sh | bash # macOS
nexus auth login
Push the repo to GitHub, then deploy:
nexus deploy source \
--repo https://github.com/you/my-app.git \
--name my-app \
--provider docker \
--framework express \
--wait
NEXUS AI builds a production image, opens a public HTTPS URL through Traefik, and streams build logs while it works. When it finishes, https://my-app.nexusai.run serves the HTML at / and JSON at /api/* from the same origin. No CORS headers needed because it is one origin.
If your HTML has no server logic at all (pure static, no API in the same process), skip the Express wrapper and deploy the folder directly:
nexus deploy source \
--repo https://github.com/you/my-static-site.git \
--name my-static-site \
--provider docker \
--framework static \
--wait
NEXUS AI detects the plain index.html and serves it with nginx.
Add environment variables and a custom domain
nexus secret create DATABASE_URL "postgres://..." --project my-app
nexus domain add my-app yourdomain.com
nexus domain verify my-app <domain-id>
That covers the common case: one small app, one deploy, one bill, free tier eligible.
Option 2: split the static site and the API into two services
Reach for this once the frontend and backend genuinely need to scale, deploy, or fail independently — a marketing site that gets Hacker-News-front-page traffic spikes while the API stays flat, or a frontend team shipping on a different cadence than the backend team.
Deploy the API on its own:
nexus deploy source \
--repo https://github.com/you/my-api.git \
--name my-api \
--provider docker \
--framework express \
--wait
Deploy the static HTML as a second service in the same project (this needs a plan with more than one active deployment — see the FAQ below):
nexus deploy source \
--repo https://github.com/you/my-frontend.git \
--name my-frontend \
--provider docker \
--framework static \
--wait
Point the frontend's fetch calls at the API's public URL, and enable CORS on the API for the frontend's origin:
// my-api/server.js
const cors = require("cors");
app.use(cors({ origin: "https://my-frontend.nexusai.run" }));
// my-frontend/app.js
fetch("https://my-api.nexusai.run/api/items")
.then((r) => r.json())
.then(render);
Each service now scales, redeploys, and rolls back independently:
nexus deploy scale my-api 3
You can also mix providers here: keep the static frontend on a CDN-first host (Vercel, Netlify, GitHub Pages) for free and put only the API on NEXUS AI. Point the frontend's NEXT_PUBLIC_API_URL (or equivalent) at the NEXUS AI URL and enable CORS the same way.
FAQ
Do I need to write a Dockerfile for a plain HTML site?
No. NEXUS AI detects an index.html with no build manifest and serves it with nginx automatically. If you have a build step (Vite, React, Vue), it detects the framework and runs the production build instead.
My API needs a database. Does that change the deploy?
No extra service to wire up separately. Add --services postgresql (or mysql, mongodb, redis) to the same nexus deploy source command and NEXUS AI provisions the database alongside your app in the same deploy, with DATABASE_URL injected automatically.
Can I use a custom domain instead of the .nexusai.run subdomain?
Yes, on any paid plan. nexus domain add <deployment> yourdomain.com, then verify the DNS record NEXUS AI gives you.
Is there a free tier?
Yes. The Free plan includes one active deployment on the NEXUS AI managed cloud, no credit card required — enough for the single-server pattern in Option 1. Running the two-service split in Option 2 on NEXUS AI for both halves needs a plan that allows more than one active deployment (Pro and above); running just the API on NEXUS AI Free while the static frontend sits on a separate free static host works within the Free plan.
Does this handle HTTPS automatically?
Yes. Every deployment gets a public HTTPS URL through Traefik by default, and custom domains get certificates provisioned automatically once DNS verification passes.
Can I deploy from the GitHub UI instead of the CLI?
Yes. Connect your GitHub account from the NEXUS AI dashboard, select a repo and branch, and enable auto-deploy so every push to that branch redeploys automatically.
What if I'd rather describe the app in plain English and skip writing server.js myself?
Use the AI App Builder instead: describe the HTML site and the API you want in chat, review the generated files and live preview, then deploy from the same dashboard.
For an app with a real database, background workers, and file storage in addition to the HTML + API pair, see From v0 prototype to production database in 5 minutes and Deploy a full-stack Python app with Postgres, Redis, and workers in 5 minutes.
Top comments (0)