RelaxNet deployment documentation
A step-by-step guide to the platform: from creating a project to setting up a database and connecting a domain.
What is RelaxNet?
RelaxNet is a platform that takes care of configuring servers, installing dependencies and keeping your application running.
Everything happens under the hood: you import code from a repository, and we automatically detect the language (Node.js, Python, React and so on), build the project and run it in a secure, isolated environment.
We build the platform for serious systems: development and previews on shared servers, staging on your team’s own isolated VM (+ Admin), production on your own server managed by the panel (Panel Prod). Details — Your own VM and server.
New features come here first, in the relaxnet.pro beta: right now that is Bot Lab and the MCP connector for AI assistants (below). Your account, projects and plan are shared with relaxdev.ru: one plan covers both panels.
Quick start
If your project uses standard settings, you can deploy it in a few clicks:
- Click Add Project.
- Pick a repository from your connected GitHub, GitLab, GitFlic or Gitverse account.
- Leave Auto-detect on — the platform will figure out how to build the app.
- Click Deploy.
In a couple of minutes the build finishes and your site is live!
Project setup in detail
For more complex apps, the new project dialog offers plenty of manual settings.
1Team
Choose the workspace (team) the project belongs to. Handy for keeping personal projects separate from team work.
2Repository and branch
Pick the source repository and branch (main or master by default). You can change the branch in settings at any time.
3Project name
Give it a clear name. It is used in the dashboard and as the basis for the system domain.
4Stack (build)
Auto-detect: the platform picks the build and start commands for you (recommended). Custom Dockerfile: choose this if your app needs a specific runtime environment.
5Environment variables (.env)
A safe place for secret keys, database passwords and API tokens. Add them manually as key–value pairs.
6Public project and Autodeploy
Public project lists it in the platform’s public catalog. Autodeploy rebuilds the project automatically on every push to the selected branch.
Platform environment variables
Some variables are handled by the platform itself. Set them in the project settings, on the Environment Variables tab.
PORT
Containers accept traffic on port 8080 only. The platform sets PORT, plus the variables for runtimes that ignore it: SERVER_PORT, UVICORN_PORT, FLASK_RUN_PORT, SANIC_PORT. Read the port from the environment instead of hard-coding it — otherwise the domain returns 502.
TG_OPTION=1
Connects the container to a network with global IPv6. Required for api.telegram.org, which our servers can’t reach over IPv4. Useless for services without AAAA records — check with dig AAAA hostname. Not needed for projects of the “Telegram Bot” type: they always have the tunnel connected.
WEB_PORT
Exposes a Telegram bot’s HTTP endpoints to the internet. By default the bot runs on long polling and the domain returns 404. The value is the port your app listens on inside the container. The endpoints become publicly reachable without authentication, so protect them in the app itself.
RELAXDEV_PROXY_URL
Routes outgoing requests through the platform proxy. Use it when an external API drops transfers from our addresses: the connection receives part of the data and then dies on a timeout, while small requests to the same host go through fine. The platform sets HTTP_PROXY, HTTPS_PROXY and NO_PROXY for the container in both upper and lower case — Python, Go, axios and curl pick them up on their own. Internal addresses (database, Redis, registry, *.relaxdev.ru) always go direct. Get the proxy address in the project: Environment Variables → Webhook / Proxy → Get proxy address. If you only need the proxy for one API, it’s easier to use it per request — see “Webhooks and proxy”.
Node with undici reads the proxy differently
Node’s built-in fetch is built on undici and ignores proxy environment variables. The same goes for Next.js server-side requests and clients like @fal-ai/client and openai. You need an explicit dispatcher — install the undici package and add this at the start of your app:
import { setGlobalDispatcher, EnvHttpProxyAgent } from 'undici';
// Читает HTTP(S)_PROXY и NO_PROXY: база, Redis и *.relaxdev.ru идут напрямую
setGlobalDispatcher(new EnvHttpProxyAgent());In Next.js, put these lines in instrumentation.ts at the project root. Java doesn’t read these variables either — add the -Djava.net.useSystemProxies=true flag to JAVA_TOOL_OPTIONS.
Build-time placeholders
The platform finds references like process.env.NAME in your source code and sets every variable that isn’t defined in the project to auto-generated-stub-for-build. That way the project always builds, and it’s safer than keeping real keys in code. But the placeholder stays in the running app too: a default value via || won’t kick in, and numbers become NaN. Only NODE_ENV and PORT are never replaced.
You can see right away which variables got a placeholder: the list with file and line is at the top of the build log, and a “Variables without a value” banner appears on the project page. Add them on the Environment Variables tab or remove the unneeded reference from the code. A server-side variable only needs a regular redeploy, while NEXT_PUBLIC_* values are baked into the browser bundle and require a full rebuild.
Need a fallback? Read the variable with square brackets: process.env['API_URL'] || 'default'. The platform leaves this form alone, so the default value works. This doesn’t work for NEXT_PUBLIC_* (Next.js only inlines dot notation into browser code), and it won’t help if variables are validated at build time, for example with zod or t3-env.
Moving from Vercel: variables and database
Variables in one paste
Export your project variables from Vercel to a file:
npx vercel link npx vercel env pull .env.vercel --environment=production
Paste the whole file into the Environment Variables tab — when creating a project or in an existing one — or upload the file itself. Quotes are stripped, Vercel’s own VERCEL_* and TURBO_* variables are skipped, and matching keys are updated. There’s a “Moving from Vercel?” hint right in the variables block. Vercel doesn’t export values marked Sensitive — enter those manually.
Database via connection string
Create a database on the Database tab. If the project already had a DATABASE_URL (from Neon, for example), it’s kept as OLD_DATABASE_URL and the new database takes its place. In the “Import from external database” block the old string is filled in for you — just click “Start Import”. You can use the string as is, pooler included: there’s no need to look for DIRECT_URL or a non-pooled address.
After the import, check your data and remove the old connection strings from the variables.
Code written for serverless
On Vercel every function lives on its own; on RelaxNet your app runs as a single long-lived process. Database client settings for serverless and the Neon pooler, like max: 1 and prepare: false, aren’t needed here: with a regular pool, page queries run in parallel.
Webhooks and proxy
The Environment Variables tab has a Webhook / Proxy section — everything about talking to external services. These are two different directions: a webhook is when an external service comes to you, a proxy is when your app goes out. The same section has the TG_OPTION=1 card — direct access to Telegram over IPv6.
Webhook receiver
Telegram, payment providers and other external services can’t always deliver webhooks directly. The receiver gives you a permanent address https://hook.relaxweb.ru/r/… on a separate server: it accepts the webhook and forwards it to your project at the path you specify, with headers unchanged. You can only forward to your own project.
- Telegram in one click. If the bot token is in your variables, “Connect to Telegram” sets the webhook through the receiver. You can see where the webhook currently points, how many updates are waiting for delivery and the last error. The secret from
TELEGRAM_WEBHOOK_SECRETarrives in theX-Telegram-Bot-Api-Secret-Tokenheader. - Each receiver shows a request counter and your project’s response to the last request.
- Address leaked? Click “Change address” — the old one stops working.
Outgoing proxy
If an external API can’t be reached from our servers — timeouts, file uploads cut off — you can route requests to it through the platform’s proxy. Click “Get proxy address” to get the address. The proxy only accepts connections from platform servers and works for HTTPS.
- Per request (recommended). Add the address to your variables under a name of your choice, e.g.
OUTBOUND_PROXY_URL, and use it only in the request that needs it. The database, Redis and all other traffic go direct. - For the whole project —
RELAXDEV_PROXY_URL. The platform setsHTTP_PROXY,HTTPS_PROXYandNO_PROXYfor the container (see the variables section above).
import { fetch, ProxyAgent } from 'undici';
const proxy = new ProxyAgent(process.env['OUTBOUND_PROXY_URL']);
const res = await fetch('https://api.example.com/...', { dispatcher: proxy });Take fetch and ProxyAgent from the same undici package: an agent from the package may not work with Node’s built-in fetch because of version differences. In Python, use the proxy parameter of the httpx client.
AI agents and MCP: Claude, Claude Code, Codex, Cursor
There’s a ready-made instruction file for AI agents: how to prepare a repository for deployment to RelaxNet and how to troubleshoot build errors — port 8080, variables and placeholders, Dockerfile, Telegram bots, webhooks and proxy, databases, moving from Vercel. Just give your agent the link.
MCP connector: Claude and Codex create and deploy bots themselves
RelaxNet has an MCP server. Once connected, an assistant (Claude, Claude Code, Codex) creates bots in Bot Lab, writes the code, deploys, reads logs and runs tests in the Telegram simulator; it redeploys projects from Git, changes their variables and works with project databases through SQL. It also sets up a permanent webhook receiver and points the Telegram bot’s webhook at it, uploads and deletes project files in storage and shows used space. You sign in with OAuth — no tokens.
- Claude (web and apps) — Settings → Connectors → Add custom connector, address
https://relaxnet.pro/api/mcp. - Claude Code — run the command below, then type
/mcpin Claude Code and sign in in your browser.
claude mcp add --transport http relaxnet https://relaxnet.pro/api/mcp
- Codex (terminal and app) — add the server and sign in: a browser opens, sign in to RelaxNet and allow access. Then start a new chat — tools load when a session starts; to check, run
codex mcp list.
codex mcp add relaxnet --url https://relaxnet.pro/api/mcp codex mcp login relaxnet
The same in ~/.codex/config.toml — a [mcp_servers.relaxnet] section with url = "https://relaxnet.pro/api/mcp". If Codex cannot get authorization data, update it; older versions need the line experimental_use_rmcp_client = true at the top of the file.
More on the Claude and MCP page.
What to tell the agent
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.
If a build fails, paste the log from the dashboard into the agent along with the same link: the instructions include a table of symptoms and causes.
Make your agent always know about RelaxNet
- Claude Code — save the instruction file in your project as
.claude/skills/relaxdev-deploy/SKILL.md: the agent picks it up on its own whenever deployment comes up. - Cursor — save it as a project rule
.cursor/rules/relaxdev.mdcor add a link to the instructions inAGENTS.md. - Any agent with internet access — put the link in your prompt, as in the example above.
What the agent won’t do for you
The agent prepares the repository: port, Dockerfile, start commands, code that reads variables. You set the environment variables, database and domain and start the build in the dashboard — the agent will tell you exactly what to add. A build starts from the button in the dashboard or with a push to the project branch.
AI assistants and search agents can also use general context about the platform: llms.txt, llms-full.txt and the skills catalog ai-catalog.json.
Git providers and deployment sources
Supported providers
github.com — account connection, deploy on push, private repositories
gitlab.com — account connection, deploy on push, private repositories
gitverse.ru — Git hosting, autodeploy, migration and sync
gitflic.ru — Git hosting, OAuth sign-in, autodeploy, migration and sync
Switching the deployment source
A project can keep repositories at several providers at once (GitHub, GitLab, Gitverse, GitFlic). On the Deploy Source tab in project settings you can switch to any linked repository without recreating the project. The change applies on the next deploy.
When you switch the source, the active branch and the provider icon in the UI update automatically.
Choosing the project folder (rootDir)
A visual folder picker for the repository is available when you create a project and in settings. Supported for GitHub, GitLab, Gitverse and GitFlic.
Handy for monorepos: deploy only the subfolder you need (apps/frontend, packages/api and so on) without changing the repository structure.
Gitverse — migration and sync
The platform supports the full workflow with Gitverse. Connect your Gitverse account in your profile, and project settings will get:
Copies a repository from GitHub or GitLab to Gitverse in one click. The source repository stays untouched.
Updates the Gitverse repo with the latest changes from GitHub/GitLab. Runs manually with the “Sync” button or automatically on autodeploy.
After migration you can switch the active deployment source to Gitverse.
GitFlic — sign-in, migration and sync
GitFlic is fully supported: you can sign in to the platform directly with your GitFlic account (OAuth) or link it with a personal token in your profile settings.
Project settings let you move a repository to GitFlic from GitHub, GitLab or Gitverse, keeping the commit history and branches. The source repository is not changed.
Deploy on push
When you create a project, the platform adds a webhook to the repository on its own — for GitHub, GitLab, GitVerse and GitFlic. From then on every push triggers a build, no clicks needed. These are Git provider webhooks; receiving webhooks in your own app is covered in “Webhooks and proxy”.
What happens after a push
- To the project branch — the new version is built and rolled out. The deployment history shows the push author and the commit.
- To another branch — a preview environment at a separate address, if previews are enabled in project settings. Delete the branch and the preview goes with it; if the provider doesn’t send a delete event, the preview is removed automatically after 72 hours.
- Autodeploy is off — a push doesn’t start a build; roll out the new version with the “Redeploy” button.
- Runners are busy — the build is queued and starts as soon as a slot frees up.
Pushed, but no build
- Check that you pushed to the branch selected in the project and that autodeploy is on.
- Open the webhooks section in the repository settings. It should list
https://relaxdev.ru/api/webhook/github(for other providers it ends ingitlab,gitverseorgitflic), and recent deliveries should have no errors. - No webhook means the connected account didn’t have permission to change repository settings, or its token expired. Reconnect the account in your profile or contact support.
Builders and stacks
The build type is detected from the repository files when the project is created. You can change it in project settings — the change applies on the next deploy.
SSR apps: build and run a Node server
frontend apps: built with npm run build
from package.json: the build and start commands
FastAPI, Flask, Django, aiohttp — from requirements.txt or pyproject.toml
from go.mod: a static binary in a small image
from composer.json, with the public folder as the web root
Maven or Gradle: from pom.xml or build.gradle
a static site with no build step, including sites from the AI builder
your own Dockerfile — for everything else
Custom Dockerfile
The Dockerfile from your repository is used only when the build type is Docker. With any other type the platform generates its own Dockerfile and yours is ignored. If changes to your Dockerfile “don’t work”, check the build type in project settings first.
While installing dependencies, the platform sets NODE_ENV=development so that devDependencies get installed. In a single-stage build this value stays in the image, and the app runs in development mode. Use a multi-stage build and set ENV NODE_ENV=production in the final stage.
Port 8080
Traffic reaches the container only on port 8080. The platform passes it in PORT, and for runtimes that don’t read PORT — in SERVER_PORT, UVICORN_PORT, FLASK_RUN_PORT and others. Listen on 0.0.0.0 and the port from the environment. After startup the builder checks whether the app responds on 8080 and writes the result to the build log: on a mismatch, the domain returns 502.
Telegram bots
- Projects of the “Telegram Bot” type have their own builder. From our servers
api.telegram.orgis reachable only over IPv6, and the required network is attached to the bot automatically. With theRELAXDEV_PROXY_URLvariable, the bot reaches Telegram through the proxy instead. - The bot runs on long polling, so a request to the project address returns 404. To expose HTTP (a Telegram webhook, a Mini App), set the
WEB_PORTvariable to the port the bot listens on. - For a web project that calls the Telegram API itself, add the
TG_OPTION=1variable or route Telegram requests through the proxy.
Prisma
If the project uses Prisma, the build runs prisma generate, and a build with database access applies the schema with prisma db push, taking the version into account: for Prisma 7 it runs without the --skip-generate flag, which no longer exists there. If the project doesn’t use Prisma, the build neither touches nor downloads it.
Hints after a build
After a failed build, the project dashboard shows a hint: what broke and how to fix it. Common errors — TypeScript types, file not found in COPY, wrong port — are recognized instantly; rare ones are analyzed by an AI model, which takes up to a minute. After a successful build, the same spot shows the list of variables without a value. The full build log is in the deployment history.
Build options
Click the arrow next to the “Redeploy” button to open the options panel.
Deploy a specific commit or branch
Paste a commit SHA, a link to a Git commit or a branch name. The platform rolls out exactly that version. Handy for rollbacks or for testing a feature branch.
abc1234 main / feature/my-branch https://gitverse.com/user/repo/commit/abc1234
Build without the Docker cache
The “No Docker cache” checkbox runs a full clean build — every layer is rebuilt from scratch. Helps when dependencies have changed but the Docker layers are stale.
Rollback
In the deployment history, click “Rollback” next to any successful deployment. The platform starts the same Docker image without rebuilding — it takes seconds.
Our own runners
Builds run on our own runners — with no GitHub Actions limits or charges.
A build starts right after a push if a runner is free.
Up to 5 builds at a time; the rest wait in the queue. Builds of the same project run one after another.
Each build runs in its own Docker container on the same server.
Build with database access
For Next.js projects with generateStaticParams that generate pages from the database at build time.
When you need it: if your Next.js site generates static pages from the database (product pages, articles, specialist profiles and so on), then without database access at build time Next.js fails with Can't reach database server.
How to enable it
- 1. Create PostgreSQL in the project’s Database section
- 2. In the Database or Settings section, turn on the Build with DB access toggle
- 3. Run a redeploy — the platform automatically switches to a dedicated builder
With this mode on, the Docker build runs in the same network as your database. DATABASE_URL is passed to the build container automatically, so generateStaticParams works correctly. For a project on your own VM or server the build runs right there, next to the team’s database.
Preview environments
A preview is a separate copy of your project, built from another branch and running in its own container at its own address. Production keeps running on its current image: a preview can’t replace it, stop it or change its domain.
Why use previews
- →Share unfinished work with a client via a link without shipping it to the live site
- →Test a risky change before it reaches users
- →Work on two tasks in parallel: two branches, two links, no interference
- →Bring up an old version from the deployment history and compare it with the current one
How to create one
- 1. Open the Environments tab in your project
- 2. Pick a branch from the list and click “Create”
- 3. In 1–3 minutes the preview is available at
project-git-branch.preview.relaxdev.ru - 4. The circular-arrow button rebuilds the preview from the latest code on the branch
Any team member with the Editor role or higher can create and delete previews — you don’t have to be the project owner.
Address and indexing
Each preview gets a third-level subdomain with an automatic HTTPS certificate. All preview addresses are hidden from search engines with the X-Robots-Tag: noindex header, so no duplicates of your site show up in search results.
Preview database
By default a preview connects to the same database as production. That’s convenient for checking layout but dangerous for branches with migrations: a schema change will hit production data. Switch the mode on the Environments tab → Variables and DB.
Same as productionThe preview works with the production database. Only suitable for changes that don’t touch data.Schema onlyThe recommended mode. The platform creates a separate database with the production schema but no data. Your app runs its own migrations and seeds.Schema and dataA separate database with the schema and a copy of the data. Takes longer to create and uses disk space.Own connection stringThe platform creates and replaces nothing — set your own DATABASE_URL in the preview variables, for example from an external provider.In the modes that create a database, the platform sets DATABASE_URL and DIRECT_URL at build time itself — values from the variables editor don’t affect the preview. The ready connection string is shown in the dashboard: you can copy it and open the database in DBeaver or DataGrip.
The database is created when the preview is built. Changing the mode deletes the old databases — rebuild the preview afterwards so the new setting takes effect. When a preview is deleted, its database is deleted with it.
Environment variables
Until you set anything for previews, they inherit the production variables. The “Copy from production” button creates a separate set that you can edit independently — for example, to use test payment keys instead of live ones.
Variables can be set for all previews at once or for a specific branch — the branch value overrides the shared one. If a variable is added to production but missing from previews, the dashboard highlights the difference and offers to add it with one click.
The “Actually applied” block shows the final set that goes into the build, with the source of each value: from production, shared across previews, a branch override or set by the platform.
Merging branches
You can merge a tested branch into production right from the dashboard, without going to your Git provider’s UI. Before merging you see how many commits ahead it is and how many files are affected. After the merge, the regular autodeploy kicks in and production updates on its own. The platform doesn’t resolve merge conflicts — fix them in the repository manually.
Merging from the dashboard works for GitHub. For GitLab, GitVerse and GitFlic, use the provider’s UI — previews themselves work the same for all four.
Lifetime and limits
- A preview lives for 72 hours; the timer restarts on every rebuild
- When it expires, the container, image and database are deleted automatically
- You can keep up to 5 previews per project at a time
- Preview resources are half of the project’s production limits
Limitations. Previews aren’t created for Telegram bots: two instances with the same token conflict when fetching updates and break the running bot. Previews are also unavailable for static HTML projects uploaded without a Git repository.
PostgreSQL databases
PostgreSQL 17 is deployed in one click. The platform creates the cluster, generates credentials, encrypts passwords and adds DATABASE_URL to your environment variables. No manual setup required.
📥 Data import
Paste a connection string to an existing PostgreSQL database — the platform takes a dump and restores the schema and data into the new database. You can use the string as is, Neon pooler included: no need to look for DIRECT_URL. If the project already had a DATABASE_URL, it’s kept as OLD_DATABASE_URL when the database is created and filled into the import form automatically. Works for moving from Neon, Supabase, Railway and Vercel Postgres.
🔒 Secure isolation
By default the database sits in a private Docker network and isn’t reachable from the internet. You can open an external port for specific IP addresses yourself.
⚡ Automatic connection
DATABASE_URL is added to your environment variables automatically when the database is created and updated when the pool settings change.
☁️ Backups to your own storage
The platform takes database copies once a day. Need fresher ones — Backups tab → “Your own storage”: any S3-compatible storage (AWS, Cloudflare R2, Backblaze, your own Garage or MinIO and others) by bucket link and keys, or a cloud drive over WebDAV (Nextcloud and others). A database copy goes there every 3, 6, 12 or 24 hours, old ones are removed after the retention period. The storage must be private: without keys we check that neither the listing nor the files open, otherwise the settings are not saved. Copies are pg_dump files (.dump) and load back via “Restore from file”.
🖥️ Our own infrastructure
Databases run on the platform’s own servers in a data center. No dependency on Neon, Supabase or Vercel Postgres.
Direct access (db push, DBeaver)
On the DB Access tab you can open an external port to connect to the database directly. For security, access is allowed only from IP addresses you specify (whitelist).
How to find your IP for the whitelist:
- Local machine (DBeaver, DataGrip): open 2ip.ru (if a VPN is on, your IP will be different).
- Cloud IDEs (Project IDX, Codespaces) or a terminal: run
curl ifconfig.me. A cloud environment’s IP is always different from your home IP, so unless you add it,prisma db pushfails with a connection error.
Connection pooling — PgBouncer
As traffic grows, so does the number of connections to PostgreSQL — and with it come latency and timeout errors. PgBouncer acts as a dispatcher: it accepts all incoming requests and spreads them across a limited number of real database connections.
Concurrent connections through the pool.
Real PgBouncer connections to PostgreSQL.
Seconds a query waits for a free connection.
DataLens — data analytics
On the DataLens tab in the database settings you can connect your project database to Yandex DataLens to build dashboards and analytics.
🔗 Direct connection
DataLens connects to PostgreSQL directly using the host, port and credentials — real-time data without exports.
🔒 Private link
A unique link with a token is generated. Without the token, the request returns 401. You can revoke and regenerate the token at any time.
⚡ Manual refresh
The “Refresh data now” button runs the SQL query immediately and updates the file in storage.
🌙 Automatic refresh
The saved SQL query runs automatically every night, so DataLens always gets fresh data.
How to connect to DataLens
- Open the DataLens tab in your project’s database settings
- Click Create a DataLens link — a token is generated
- Write an SQL query and click Refresh data now
- Copy the link with the token
- In DataLens: Create connection → Files → paste the link
https://relaxdev.ru/api/projects/{id}/datalens-export?token={token}
Available on the PRO, Pro Team and BUSINESS plans.
Redis
Managed Redis 7 — one container per team, connected in one click right from the project dashboard.
✅ Available on all plans, including FREE
⚡️ Once connected, REDIS_URL appears in the project’s environment variables automatically.
🔒 Each team gets an isolated instance with its own password.
🔄 Supports caching, sessions, queues, pub/sub and rate limiting.
📡 For Socket.io with multiple replicas, use the Redis Pub/Sub adapter — the platform already provides the connection.
# The variable is added automatically
REDIS_URL=●●●●●●●●●●●●●●●●
Socket.io with multiple replicas:
// Синхронизация реплик через Redis адаптер
import { createAdapter } from "@socket.io/redis-adapter"
import { createClient } from "redis"
const pub = createClient({ url: process.env.REDIS_URL })
const sub = pub.duplicate()
io.adapter(createAdapter(pub, sub))
Domains and SSL
Every project gets a {name}.relaxdev.ruaddress with HTTPS right away. To connect a custom domain, add two A records and then the domain itself on the Domains tab — the Let's Encrypt certificate is issued and renewed automatically. For a detailed walkthrough with examples, see the article “Domains and DNS on RelaxNet” (in Russian).
Records at your registrar
| Type | Name | Value |
|---|---|---|
| A | @ | 72.56.37.162 |
| A | www | 72.56.37.162 (or a CNAME to the main domain) |
The name must have no other A or AAAA records. NS records should be the original ones — at your registrar or in Cloudflare. Leave mail records (MX, SPF, DKIM) alone: they have nothing to do with the site. If the domain has a CAA record, it must allow letsencrypt.org.
Connection steps
- Add the records at your registrar or in Cloudflare.
- Check them on dnschecker.org: in every region, the A record should show only
72.56.37.162. - Add the domain on the Domains tab. The dashboard checks DNS through public resolvers itself: if the records don’t point to us yet, it shows what’s wrong and won’t add the domain without confirmation.
- The certificate is issued automatically, usually within a minute.
Don’t leave a domain with wrong records
A domain added to a project immediately gets a certificate order. If the records don’t point to us, the attempts fail, and after five failures per hour for the same name, Let's Encrypt temporarily stops issuing certificates for it. Records not ready? Remove the domain from the project, fix DNS and add it again. Still no HTTPS after an hour — contact support.
↔️ www or no www
Add both names and choose the primary one: www redirects to the bare domain, the other way round, or both serve the site. The secondary name returns a permanent 301 redirect. Applied without a rebuild.
↩️ Domain redirects
A permanent redirect of a whole domain to another address — an old name to a new one, a typo to the main one. The path and query can be preserved. Remove it at any time and the domain serves the project again.
🔀 Subdomains
app.example.ru is added as a separate domain with its own A record. To search engines, a subdomain is a separate site; if your goal is to promote a single site, a section like example.ru/app works better.
✳️ Wildcard
All *.example.ru subdomains at once with a single certificate — for services where every customer gets their own subdomain. PRO Team plan. The dashboard shows the required records (a CNAME for _acme-challenge and two A records) when you turn it on, and checks DNS itself.
🔤 Cyrillic domains
Enter пример.рф as is: it’s stored in punycode (xn--…), displayed in Cyrillic, and the records are the same. To make Google show the name in search results instead of xn--…, add WebSite structured data to the home page.
Common issues
- Cloudflare. Records must be set to “DNS only” (grey cloud). In proxied mode, Cloudflare’s SSL settings cause redirect loops and block certificate issuance.
- Moving from Vercel. Remove the domain in Vercel (Settings → Domains → Remove), delete the
76.76.21.21,216.198.79.1andcname.vercel-dns.comrecords, and if your NS records pointed to Vercel, restore your registrar’s NS or move them to Cloudflare. - Records changed, but the old site still opens. Resolvers remember the previous value until the TTL expires (at least an hour at Reg.ru). You can flush the cache at Google and Cloudflare.
- The registrar panel looks right, but DNS returns an extra address. Sometimes a registrar serves a record that isn’t visible in its UI. The reliable fix is to move NS to Cloudflare.
Resource monitoring
Your projects run inside isolated virtual machines (VMs). Transparent monitoring tools are available:
CPU & RAM
Track CPU and memory usage. Helps you spot memory leaks in your code early.
Network (Net I/O)
A chart of incoming and outgoing traffic for analyzing user activity.
Runtime Logs
Real-time application logs. An essential tool for finding and fixing errors.
Auto-restart
If a project runs out of available memory, the system restarts the container automatically.
File storage and Storage API
Every project has its own folder in object storage. You can upload, list and delete files from code using the STORAGE_API_KEY token (the API access tab in the file manager). All project files live under the users/<id>/<project>/ prefix in shared storage and are isolated from other users. <id> is the account id of the project creator, so links carry no email; projects created earlier keep an email there and their links did not change. Take the path and link from the API response (path, url) instead of building them.
The file manager has an Optimize to WebP checkbox (on by default): images are compressed right in the browser before they are sent to storage.
Where to keep the key
- Inside the platform — you don’t need to store it. When you click “Generate key”, it’s written to the project’s environment variables (
envVars) automatically. After “Start redeploy”, the token is passed into the container and is available in code asprocess.env.STORAGE_API_KEY. - Locally or in external services — you do need to store it. Copy the key and put it in your local
.env:STORAGE_API_KEY=sk_rd_... - Forgot the key? Open “API access”, click 👁️ and copy it again — it always lives in
envVars.
POST — upload a file
Raster images (JPEG, PNG, BMP, TIFF) are automatically optimized to WebP by default — usually 2–4× smaller with no visible loss of quality. The conversion runs on the platform side, the file extension changes to .webp, and the response includes a webp field. To upload a file as is, pass -F "webp=false". GIF, SVG and files that are already WebP are left untouched.
curl -X POST https://relaxdev.ru/api/v1/storage/upload \
-H "Authorization: Bearer $STORAGE_API_KEY" \
-F "file=@./photo.jpg" \
-F "path=avatars" # необязательная подпапка
# -F "webp=false" # отключить оптимизацию (по умолчанию webp=true)
# Ответ:
# {
# "success": true,
# "url": "https://cdn.relaxdev.ru/users/<id>/<имя-проекта>/avatars/1700000000000-photo.webp",
# "fallbackUrl": "https://files.relaxdev.ru/users/<id>/<имя-проекта>/avatars/1700000000000-photo.webp",
# "path": "users/<id>/<имя-проекта>/avatars/1700000000000-photo.webp",
# "webp": true
# }The same request from Node.js / Next.js server code:
const form = new FormData();
form.append("file", fileBlob, "photo.jpg");
form.append("path", "avatars");
// form.append("webp", "false"); // по умолчанию оптимизация включена
const res = await fetch("https://relaxdev.ru/api/v1/storage/upload", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.STORAGE_API_KEY}` },
body: form,
});
const { url, fallbackUrl, webp } = await res.json();GET — list files
curl "https://relaxdev.ru/api/v1/storage/files?path=avatars" \
-H "Authorization: Bearer $STORAGE_API_KEY"
# Ответ:
# {
# "success": true,
# "files": [
# { "path": "users/<id>/<имя-проекта>/avatars/...jpg",
# "url": "https://cdn.relaxdev.ru/users/<id>/<имя-проекта>/avatars/...jpg",
# "fallbackUrl": "https://files.relaxdev.ru/users/<id>/<имя-проекта>/avatars/...jpg",
# "size": 20413, "lastModified": "..." }
# ]
# }DELETE — delete a file
Pass the full path from the upload or list response. You can only delete files inside your own project’s folder.
curl -X DELETE "https://relaxdev.ru/api/v1/storage/files?path=users/<id>/<имя-проекта>/avatars/1700000000000-photo.jpg" \
-H "Authorization: Bearer $STORAGE_API_KEY"
# Ответ: { "success": true, "deleted": "users/<id>/<имя-проекта>/avatars/1700000000000-photo.jpg" }
# Вместо path можно передать ссылку на файл — с cdn.relaxdev.ru или files.relaxdev.ruYour own VM (+ Admin) and your own server (Panel Prod)
Staging on the platform and production on your own server — project environments: instances with their own database and domains, shown as tabs in the project, deploying the same commit. Everything in one place — what to choose, environments, moving, domains and databases: Your own VM and server.
PRO, PRO Team, Business
planProjects on the platform’s shared servers.
For development, testing and previews.
as per plan
+ Admin
add-onYour team’s own VM on the platform’s servers, SSH without root.
Only on top of a plan: PRO + Admin, PRO Team + Admin, Business.
from $6 to $48/month
Panel Prod
your serverYour own server managed by the panel — for production.
A separate plan, with or without a main one. Logs and database dumps are not kept on our side.
$12/month
+ Admin — your team’s own VM
An add-on to PRO, PRO Team or Business — it can’t be ordered on its own. A dedicated virtual machine for your team on the platform’s servers: its resources are yours alone and neighbouring projects don’t affect it.
- Size
- 1–4 vCPU, 2–8 GB of memory, 20–100 GB of disk — you choose when ordering.
- Price
- $3 per vCPU + $2 per GB of memory + $0.30 per GB of disk above 20 GB, charged daily from your balance.
- Addresses
- The VM has no IP of its own — it sits behind the platform’s shared entry point 135.136.191.56. Projects get <name>.vm.relaxdev.ru automatically; a custom domain is an A record pointing to 135.136.191.56, added in the Domains tab.
- Access
- SSH with your key, without root. Docker, Traefik, certificates and updates are handled by the platform.
How to order
- VM page → + Admin · your own VM or the “Own VM · + Admin” block in project settings. The team’s first owner orders it — on their plan and balance.
- Pick a size, paste a public SSH key (
ssh-ed25519 …orssh-rsa …) and a login. Your balance must cover a month of the chosen VM — only the first day is charged right away. - The VM is created in 5–15 minutes, then you get an email with the login command. If creation fails, the money goes back to your balance.
Projects and databases
- New team projects start on the VM. A running project moves from its settings: the code is rebuilt from Git, the database is copied, Redis starts empty, the old address answers with a 301, custom domains move along. If the build fails, the move rolls back by itself.
- The team’s PostgreSQL cluster runs on the VM. To connect from your computer, DBeaver or CI, use external access with an IP allowlist — in the project’s Database tab.
- Databases on the VM are backed up every night and kept for 3 days. A manual backup is in the database’s Backups tab.
- A custom domain for a project on the VM is an A record pointing to
135.136.191.56; the certificate is issued automatically. It is the shared entry point of all VMs: a bare IP won’t open a site, projects are told apart by domain name. - The PgBouncer pool, database import by link and builds with database access work on VMs too; DataLens isn’t available yet.
SSH and console
- The login command is on your VM card on the VM page, like
ssh -p 22NN c-login@135.136.191.56. One key; you can replace it there. - Over SSH: VM processes (
top,ps), your home folder, git, your own tools in~/binandpip --user, file transfer withscp, internet access. - No root, Docker access or port forwarding: containers, Traefik and certificates are run by the platform, so they can’t be broken by accident.
- Container logs and controls are in the console on the VM page:
docker ps,stats,logs, restart and stop (team owner and editor). Logs, metrics, deployments and rollbacks are in the project tabs.
Payment and cancellation
- Out of money — you get an email; 24 hours later the VM shuts down and its disk is kept for 14 days. After a top-up the VM starts again by itself.
- You can cancel on the VM page at any time: the VM shuts down and charges stop. The disk is kept for 14 days, then the VM is deleted with its data.
Panel Prod — your own server
Your server managed by the panel: builds, deploys, custom domains and certificates work just like on our VMs. Logs are not sent to the platform, and the platform does not make or keep database dumps and backups. A separate plan — with or without a main one; plan limits don’t apply on your server.
What you need
- An x86_64 server with Ubuntu or Debian and a public IPv4 address: SSH open, ports 80 and 443 free.
- SSH login details, once: IP, port, root or a dedicated sudo user (e.g.
relaxdev), a password or a private key. The panel installs Docker and Traefik, creates therelaxuser with its own key and signs in as that user from then on. Your login details are not stored. - A domain for project addresses, for example
apps.example.com.
Domains and addresses
- The domain you give when connecting is only for technical addresses: every project gets
<name>.apps.example.comwith an HTTPS certificate. That takes two A records pointing to the server IP:apps.example.comand*.apps.example.com. - Your projects’ own domains (
example.com,www.example.com) are added as usual in the Domains tab — an A record pointing to your server’s IP; the certificate is issued automatically. - A bare IP won’t open a site: several projects share the server and Traefik tells them apart by domain name; HTTPS certificates are issued for domain names.
Projects and databases
- New apps and HTML sites of the team start on the server. A running project moves from its settings, block “Own VM or own server”: the code is rebuilt and the database copied.
- Already have your own VM? Panel Prod connects alongside it. Projects move from the VM to the server all at once (the team’s database and Redis are shared): VM page → Panel Prod → “Move the whole VM”. If any build fails, everything returns to the VM; after the move you can cancel the VM.
- The team’s PostgreSQL cluster runs on your server; database backups are on your side.
- Logs — the latest lines straight from the server in the project tab.
How to connect
- VM page → Panel Prod. The team’s first owner connects it — charges go to them.
- Your balance must cover a month; only the first day is charged right away.
- Setting up the server takes a few minutes. If it fails, the charge goes back to your balance.
Support
The dashboard has a full-featured built-in support chat.
- History: your entire conversation history is saved.
- Files: you can attach screenshots of logs or code snippets right in the chat.
- We’re always happy to help with build errors, databases and environment setup.