Docs / Deploy
Deploying a project: from repository to custom domain
Connect a repository — the platform builds a Docker image, starts a container and gives you a name.relaxdev.ru address with HTTPS. Every push to the branch rolls out a new version by itself.
Below, step by step: the first deploy, what to change in code, variables, rollback, logs, common errors and the domain.
How it works
- 1
Push to the project branch
The Git provider sends a webhook — the platform adds it to the repository itself when the project is created. - 2
Build on a runner
Every build runs in its own container. Up to 5 builds run at once; builds of one project run one after another. - 3
Docker image
The platform generates a Dockerfile for the stack or uses yours if the build type is Docker. - 4
Container start
The new version gets traffic on port 8080; the address and certificate stay the same.
Your first deploy, step by step
- 1
Connect a Git account
In your profile: GitHub, GitLab, GitFlic or Gitverse. Private repositories are supported. - 2
Click Add Project and pick a repository
The default branch is main or master; you can change it in the settings at any time. - 3
Pick a folder if it’s a monorepo
Choose a subfolder in the folder picker, for exampleapps/web. The rest of the repository stays out of the build. - 4
Keep “Auto-detect” on
The platform figures out how to build the app from the repository files. For your own Dockerfile, choose the Docker build type. - 5
Add variables
Paste the whole.envinto the Variables tab — keys and values are parsed for you. A database can be connected later, step by step. - 6
Click Deploy
In a couple of minutes the project opens atproject-name.relaxdev.ruwith HTTPS. You can follow the build in the log.
Preparing the app
The main rule is port 8080 from the PORT variable and the 0.0.0.0 address. Traffic reaches the container only on 8080. The platform passes it in PORT, and to runtimes that don’t read PORT — in SERVER_PORT, UVICORN_PORT, FLASK_RUN_PORT, SANIC_PORT. Listen on another port or on localhost and the domain returns 502.
Next.js
next start reads PORT by itself — nothing to change.
{
"scripts": {
"build": "next build",
"start": "next start"
}
}Node.js — Express, Fastify, NestJS
import express from "express";
const app = express();
app.get("/health", (req, res) => res.send("ok"));
const port = Number(process.env.PORT) || 8080;
app.listen(port, "0.0.0.0", () => console.log(`listening on ${port}`));Python — FastAPI, Flask, Django
# main.py — FastAPI
import os
import uvicorn
from fastapi import FastAPI
app = FastAPI()
@app.get("/health")
def health():
return {"ok": True}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "8080")))Go
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
log.Fatal(http.ListenAndServe(":"+port, mux))The app is one long-running process
This isn’t serverless: database connections, in-memory cache and background timers live as long as the container runs. Vercel-style settings like max: 1 in the database pool aren’t needed.
Builders and stacks
The build type is detected from the repository files when the project is created. You can change it in the settings — the change applies on the next deploy.
| Stack | How it’s built |
|---|---|
| Next.js, Nuxt, TanStack Start | SSR app: build and run a Node server |
| Vite, React, Angular | Frontend: built with npm run build |
| Node.js, NestJS, Express | From package.json: the build and start scripts |
| Python | FastAPI, Flask, Django, aiohttp — from requirements.txt or pyproject.toml |
| Go | From go.mod: a static binary in a small image |
| PHP, Laravel | From composer.json, the site root is the public folder |
| Java | Maven or Gradle: from pom.xml or build.gradle |
| HTML | A static site without a build |
| Telegram Bot | Its own builder: the network to api.telegram.org is attached automatically |
| Docker | Your Dockerfile — for everything else |
If the project uses Prisma, the build runs prisma generate. Need the database during the build (static pages from the database, prisma db push)? Turn on Build with DB access.
Your own Dockerfile
The repository’s Dockerfile applies only when the build type is Docker. While dependencies install, the platform sets NODE_ENV=development so devDependencies get installed. Use a multi-stage build and set NODE_ENV=production in the final stage — otherwise the app stays in development mode.
# Build
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Run
FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=8080
COPY --from=build /app/package*.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
EXPOSE 8080
CMD ["node", "dist/server.js"]# Python
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8080
CMD ["sh", "-c", "uvicorn main:app --host 0.0.0.0 --port ${PORT:-8080}"]Environment variables
- 1
Open the project’s Variables tab
Add them one by one or paste a whole .env — you can also upload the file. Quotes are stripped, matching keys are updated. - 2
Save and click Redeploy
A restart is enough for server variables.NEXT_PUBLIC_*are baked into the browser code at build time — for them, click Rebuild.
# Paste into the Variables tab as a whole
API_URL=https://api.example.com
TELEGRAM_BOT_TOKEN="123456:ABC..."
NEXT_PUBLIC_SITE_NAME=My storeMoving from Vercel
npx vercel link
npx vercel env pull .env.vercel --environment=production
# paste the contents of .env.vercel into the Variables tabService variables VERCEL_* and TURBO_* are skipped. Vercel doesn’t export values marked Sensitive — enter those by hand.
Build stubs
The platform finds process.env.NAME references in the source and gives variables missing from the project the value auto-generated-stub-for-build — so the project builds no matter what. But the stub stays in the running app too: a default via || won’t kick in, and numbers become NaN. Only NODE_ENV and PORT are never stubbed.
- Which variables got a stub is listed at the top of the build log and in the “Variables without a value” banner on the project page.
- Need a fallback? Read with square brackets:
process.env['API_URL'] || 'default'. The platform leaves this form alone. It doesn’t work forNEXT_PUBLIC_*: Next.js inlines only the dot notation into the browser code.
Service variables
| Variable | What for |
|---|---|
PORT | Set by the platform: 8080. Read the port from it. |
TG_OPTION=1 | IPv6 network for a web project that calls api.telegram.org itself — our servers can’t reach it over IPv4. Not needed for the Telegram Bot project type. |
WEB_PORT | Exposes a Telegram bot’s HTTP (a webhook, a Mini App). The value is the port the bot listens on. |
RELAXDEV_PROXY_URL | Outgoing requests through the platform proxy — when an external API drops transfers from our addresses. The address is under Variables → Webhook / Proxy. |
More about the proxy and webhooks — in the docs.
Autodeploy on push
- Push to the project branch — a build and a rollout of the new version. The deployment history shows the author and the commit.
- Push to another branch — a preview environment at its own address, if previews are on in the project settings.
- Autodeploy off — a new version rolls out with the Redeploy button.
Pushed, but no build
- 1
Check the branch
The push must go to the branch selected in the project, and autodeploy must be on. - 2
Check the webhook in the repository
In the repository settings, the webhooks section: the addresshttps://relaxdev.ru/api/webhook/github(other providers end ingitlab,gitverseorgitflic), recent deliveries without errors. - 3
Reconnect Git
No webhook means the account lacked permission for the repository settings or the token expired. Reconnect Git in your profile or contact support.
Deploying from GitLab, GitFlic and Gitverse works the same way. A repository can be moved to GitFlic or Gitverse from the project settings and the deployment source switched — details.
Redeploy, rebuild and rollback
| Action | What happens | When you need it |
|---|---|---|
| Redeploy | New commits — a fresh build; none — the current build restarts with the new variables | After changing variables or connecting a database |
| Rebuild | A full build from the branch | NEXT_PUBLIC_* changed, the build type changed |
| Commit or branch | The arrow next to Redeploy: paste a SHA, a commit link or a branch name | Roll out a specific version |
| No Docker cache | A checkbox in the same panel: every layer is rebuilt | Dependencies updated but the layers stayed old |
| Rollback | In the deployment history: the same image starts without a build, in seconds | The new version broke |
| Restart without a build | The button next to Redeploy; stop and start are there too | The app hangs |
Logs and common errors
- Build log — in the deployment history. After a failed build the dashboard shows a hint: what broke and how to fix it. Common errors are recognized instantly; rare ones are analyzed by an AI model — up to a minute.
- App logs — everything the app writes to stdout and stderr; kept for 7 days on the platform.
- Monitoring — CPU, memory and network charts. If the app runs out of memory, the container restarts by itself.
| Symptom | Cause and fix |
|---|---|
| 502 Bad Gateway | The app listens on a port other than 8080 or on localhost. Read PORT, listen on 0.0.0.0. |
| Can't reach database server during the build | Pages are built from the database at build time — turn on Build with DB access. |
| TypeScript errors in the log | The build checks types like next build does on your machine. Fix the errors or check locally with npx tsc --noEmit. |
| COPY failed: file not found | A path in the Dockerfile doesn’t match the project folder. Check the selected folder (rootDir) and the COPY paths. |
The value auto-generated-stub-for-build, NaN | The variable is missing from the project — add it on the Variables tab. |
| Telegram bot: the project address returns 404 | The bot runs on long polling. To expose HTTP, set WEB_PORT. |
| An external API drops on a timeout | Route the requests through the proxy: RELAXDEV_PROXY_URL. |
Branch previews
- 1
Open the Environments tab
Pick a branch and click Create. - 2
Open the preview address
In 1–3 minutes —project-git-branch.preview.relaxdev.ru, hidden from search engines. Production keeps running on its image. - 3
Choose the preview database
By default it’s the same as production. For branches with migrations choose “Schema only”: a separate database with the production schema and no data.
A preview lives for 72 hours; the timer resets on every rebuild. Up to 5 previews per project. Preview variables and branch merging in detail — in the docs.
Custom domain
- 1
Add the records at your registrar
An A record@→72.56.37.162and an A recordwww→72.56.37.162(or a CNAME to the main domain). The name must have no other A or AAAA records. In Cloudflare — “DNS only” mode. - 2
Check DNS
On dnschecker.org, A must show only72.56.37.162in every region. For a project on your own VM or server, the Domains tab shows the address. - 3
Add the domain on the Domains tab
The dashboard checks DNS itself and won’t add the domain until the records point to us. - 4
Wait for the certificate
Let’s Encrypt issues it automatically, usually within a minute, and renews it by itself.
The same tab has the primary name (with or without www), domain redirects, subdomains, wildcard and Cyrillic domains: more about domains.
Deploying with an AI agent and MCP
Prepare the repository
Give your agent (Claude Code, Cursor, Codex) the guide link — it fixes the port, the Dockerfile and the code for variables:
Prepare this project for deployment to RelaxNet following
https://relaxdev.ru/.well-known/skills/relaxdev-deploy/SKILL.md
and list what I need to configure in the dashboard.Deploy and read logs over MCP
The https://relaxnet.pro/api/mcp connector works in Claude, Claude Code and Codex with your usual account and projects. The assistant starts a deploy, waits for the result, reads build and app logs, changes variables and build settings, and works with databases.
claude mcp add --transport http relaxnet https://relaxnet.pro/api/mcp
codex mcp add relaxnet --url https://relaxnet.pro/api/mcp && codex mcp login relaxnetDeploy the shop project and wait for the result. If the build fails, go through the log and suggest a fix.
Show the last 200 lines of the shop project's logs with errors.
Add API_URL=https://api.example.com to the shop project and restart it.In detail — databases over MCP and Claude, Codex and MCP.
FAQ
How long does a build take?+
Usually 1–5 minutes, depending on the dependencies and the stack. Repeat builds are faster — Docker layers come from the cache. If the runners are busy, the build waits in the queue and starts as soon as a slot frees up.
The domain returns 502 Bad Gateway+
The app isn’t listening on port 8080. Read the port from the PORT variable and listen on 0.0.0.0, not localhost. The builder writes the port check result to the build log.
I changed a variable, but the app sees the old value+
Click Redeploy. Server variables are picked up on restart, while NEXT_PUBLIC_* are baked into the browser code at build time — for those, click Rebuild.
My Dockerfile changes don’t apply+
The repository’s Dockerfile is used only when the build type is Docker. With any other build type the platform generates the Dockerfile itself. Check the build type in the project settings.
How do I roll back to the previous version?+
In the deployment history, click Rollback next to the successful build you need. The platform starts the same image without a rebuild — it takes seconds.
Can I deploy without GitHub?+
Yes: GitLab, GitFlic and Gitverse work the same way, with autodeploy on push. A static site can be uploaded as an HTML project, and a Telegram bot can be built in Bot Lab without a repository.
I pushed, but no build started+
Check that the push went to the project branch and autodeploy is on. The repository settings must have the webhook https://relaxdev.ru/api/webhook/github (or gitlab, gitverse, gitflic) without delivery errors. No webhook — reconnect Git in your profile.