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.

Contents

How it works

  1. 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. 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. 3

    Docker image

    The platform generates a Dockerfile for the stack or uses yours if the build type is Docker.
  4. 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. 1

    Connect a Git account

    In your profile: GitHub, GitLab, GitFlic or Gitverse. Private repositories are supported.
  2. 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. 3

    Pick a folder if it’s a monorepo

    Choose a subfolder in the folder picker, for example apps/web. The rest of the repository stays out of the build.
  4. 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. 5

    Add variables

    Paste the whole .env into the Variables tab — keys and values are parsed for you. A database can be connected later, step by step.
  6. 6

    Click Deploy

    In a couple of minutes the project opens at project-name.relaxdev.ru with 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.

package.json
{
  "scripts": {
    "build": "next build",
    "start": "next start"
  }
}

Node.js — Express, Fastify, NestJS

javascript
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

python
# 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

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.

StackHow it’s built
Next.js, Nuxt, TanStack StartSSR app: build and run a Node server
Vite, React, AngularFrontend: built with npm run build
Node.js, NestJS, ExpressFrom package.json: the build and start scripts
PythonFastAPI, Flask, Django, aiohttp — from requirements.txt or pyproject.toml
GoFrom go.mod: a static binary in a small image
PHP, LaravelFrom composer.json, the site root is the public folder
JavaMaven or Gradle: from pom.xml or build.gradle
HTMLA static site without a build
Telegram BotIts own builder: the network to api.telegram.org is attached automatically
DockerYour 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.

dockerfile
# 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"]
dockerfile
# 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. 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. 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.
.env
# Paste into the Variables tab as a whole
API_URL=https://api.example.com
TELEGRAM_BOT_TOKEN="123456:ABC..."
NEXT_PUBLIC_SITE_NAME=My store

Moving from Vercel

bash
npx vercel link
npx vercel env pull .env.vercel --environment=production
# paste the contents of .env.vercel into the Variables tab

Service 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 for NEXT_PUBLIC_*: Next.js inlines only the dot notation into the browser code.

Service variables

VariableWhat for
PORTSet by the platform: 8080. Read the port from it.
TG_OPTION=1IPv6 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_PORTExposes a Telegram bot’s HTTP (a webhook, a Mini App). The value is the port the bot listens on.
RELAXDEV_PROXY_URLOutgoing 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. 1

    Check the branch

    The push must go to the branch selected in the project, and autodeploy must be on.
  2. 2

    Check the webhook in the repository

    In the repository settings, the webhooks section: the address https://relaxdev.ru/api/webhook/github (other providers end in gitlab, gitverse or gitflic), recent deliveries without errors.
  3. 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

ActionWhat happensWhen you need it
RedeployNew commits — a fresh build; none — the current build restarts with the new variablesAfter changing variables or connecting a database
RebuildA full build from the branchNEXT_PUBLIC_* changed, the build type changed
Commit or branchThe arrow next to Redeploy: paste a SHA, a commit link or a branch nameRoll out a specific version
No Docker cacheA checkbox in the same panel: every layer is rebuiltDependencies updated but the layers stayed old
RollbackIn the deployment history: the same image starts without a build, in secondsThe new version broke
Restart without a buildThe button next to Redeploy; stop and start are there tooThe 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.
SymptomCause and fix
502 Bad GatewayThe 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 buildPages are built from the database at build time — turn on Build with DB access.
TypeScript errors in the logThe build checks types like next build does on your machine. Fix the errors or check locally with npx tsc --noEmit.
COPY failed: file not foundA 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, NaNThe variable is missing from the project — add it on the Variables tab.
Telegram bot: the project address returns 404The bot runs on long polling. To expose HTTP, set WEB_PORT.
An external API drops on a timeoutRoute the requests through the proxy: RELAXDEV_PROXY_URL.

Branch previews

  1. 1

    Open the Environments tab

    Pick a branch and click Create.
  2. 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. 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. 1

    Add the records at your registrar

    An A record @ → 72.56.37.162 and an A record www → 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. 2

    Check DNS

    On dnschecker.org, A must show only 72.56.37.162 in every region. For a project on your own VM or server, the Domains tab shows the address.
  3. 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. 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:

text
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.

bash
claude mcp add --transport http relaxnet https://relaxnet.pro/api/mcp
codex mcp add relaxnet --url https://relaxnet.pro/api/mcp && codex mcp login relaxnet
text
Deploy 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.