A year on Vercel and one evening of moving: bazariara.ge has become faster and is opening again in Russia

A year on Vercel and one evening of moving: bazariara.ge has become faster and is opening again in Russia

relaxdev 03/10/2026

Bazari Arais an online store of Georgian products, gifts and travel equipment in Tbilisi. Next.js 16, PostgreSQL, images in object storage. The project is about a year old, and all this year he lived on Vercel with a base in Neon.

We transferred it to RelaxDev and did not expect such a result:

  • ✅100 out of 100on three checks: PageSpeed Insights, speed and site analysis in PR-CY;
  • ⚡ from Tbilisi the server responds for0.12–0.14 sinstead of 1.5–3.5 s;
  • 🇷🇺 the site opens again from Russia, from Moscow the answer is0.02–0.03 s;
  • 🪶 the page is 7 times lighter, the main HTML is three times lighter, the picture in the catalog is 10 times lighter.

Result: 100 out of 100Result: 100 out of 100


Why did you leave Vercel?

ProblemWhat this meant for the store
🚫Site not available in RussiaRegular providers and mobile operators did not open their stores. Main reason for moving
🗄Base limitsNeon's free plan had limits, and I didn't want to pay for the database separately from hosting
🧊Aggressive cacheVercel gave away old product cards for a long time: the price or availability was changed, but visitors see the same
🖼Quota for image optimizationImage optimization had to be turned off to avoid burning out the quota. The catalog gave away originals at 100–450 KB
🐢Cold startThe first visitor after a pause waited 1.5–3.5 seconds for the server’s response

Where to move: Russia, Türkiye or Poland

Almost all buyers are from Georgia, some are from Russia. Before moving, we measured all the options: pings between servers, requests from servers and Globalping - three points each in Tbilisi (Magticom, TradeZone, Cloud 9) and three in Moscow and St. Petersburg (Yandex.Cloud, Timeweb, Selectel).

Candidates: -Vercel— where the site lived; -Russia— RelaxDev, our server in Moscow; -Türkiye- test server: you can move the application there or allow visitors through it; -Polandis the European node of RelaxDev, through which you can also receive visitors.

Pings between servers

From → toPing, averageScatter
Moscow → Türkiye83 ms78–87 ms
Türkiye → Moscow79 ms78–80 ms
Moscow → Vercel65ms46–133 ms
Türkiye → Vercel10ms9–11 ms

Requests to the site from servers

Three requests in a row. Here we measured a redirect response from / to /ru, so this is the time of the server, not the entire page.

From → toConnectionFirst byte
Moscow → Vercel48–199 ms0.24–0.88 s
Türkiye → Vercel43–69 ms0.20–0.40 s
Türkiye → RelaxDev in Russia0.25–5.16 s0.84–5.36 s

The last line is the main discovery. The “Türkiye → Russia” channel is unstable on new connections: connection sometimes takes five seconds.

How close is each site to customers?

Globalping, connection time (TCP) - pure network, no server running.

Playgroundfrom Tbilisifrom Moscow and St. Petersburg
Vercel (point of presence right in Tbilisi)2–39 ms6–87 ms
Türkiye30–99 ms77–95 ms
Poland71–82 ms42–72 ms
Russia, RelaxDev directly85–121 ms0–12 ms

Vercel and Türkiye are the closest to Georgia on the network. But the network is only part of the answer.

How long to wait for the first byte of the page

Routefrom Tbilisifrom Moscow and St. Petersburg
Vercel1.56–3.54 sec0.46–0.67 s
Via a server in Turkey0.29–0.56 s0.66–1.14 s
Via node in Poland¹0.15–0.16 s0.12–0.14 s
Russia, RelaxDev directly0.12–0.14 s0.02–0.03 s

For comparison: a static page served by nginx directly from a Turkish server takes 0.04–0.09 s from Tbilisi. A server in Turkey is good, but only if everything lives on it at once.

¹ Measurement on another platform project, which is already working through the Polish node. The direct route to Russia was also measured there: 0.10–0.13 s from Tbilisi - the same numbers as bazariara.

The base must be next to the application

We tested this on ourselves. While the application was already working in Russia, the base remained in Neon (Frankfurt):

MeasurementResult
Connecting to Neon from Russia0.8 s only to establish a connection
Page from Tbilisi, cold cache8.5–9.6 sec
The same page from Moscow, the cache is already warmed up0.13 s
pg_dump from Neon directly to Russiastuck, interrupted manually
pg_dump via server in Poland12.6 sec

Each page request to a database abroad paid for the journey there and back, so first we transfer the base and only then measure the speed.

Why not Türkiye

-The site on the Turkish server could not be opened from the mobile Internet in Russia.Russian mobile operators have poor connections with foreign hosting providers. We would have to separate visitors by country via GeoDNS. -The intermediate server in Turkey only adds the path:from Tbilisi the first byte is 0.29–0.56 s versus 0.12–0.14 directly, from Moscow - up to a second. -Channel “Türkiye → Russia” is unstable:connection from 0.25 to 5.2 seconds. -If you keep the application itself in Turkey, the base also moves there.It turns out a second site and a second base. -Network gain for Georgia is 30-60 ms.This is not worth a second site, second base and GeoDNS.

Why not Poland

The Polish knot is an extra shoulder on the way. For Georgia, it is slower than the direct one: the TLS handshake goes through Poland, 219–241 ms versus 106–136 directly. For visitors from Russia, it also adds a delay.Conclusion: The easiest and fastest way is to go directly to Russia, to RelaxDev. Georgia receives the first byte in 0.12–0.14 s, Russia in 0.02–0.03 s, the site and base are the same.


How we moved

1. Environment variables - with one command

Bash
1
2
npx vercel link

npx vercel env pull .env.vercel --environment=production

The entire file is imported in the “Variables” tab. The quotation marks are removed by themselves, the service VERCEL_* and TURBO_* are skipped.

Import variables from Vercel in the “Variables” tabImport variables from Vercel in the “Variables” tab

  • Import variables from file .env.vercel*

2. Base - import by connection string

In the “Database” tab there is an import from another database: we inserted the Neon connection string, and the data moved to the platform database. The platform itself added the connection string to the new database to the project variables.

Import database from Neon using connection stringImport database from Neon using connection string

  • Import database from Neon using connection string*

3. Code - changes for a permanent server

On Vercel, each function lives separately; on RelaxDev, the application runs as one long-lived process.

Was (under Vercel)BecameWhy
ssl: 'require' alwaysSSL by connection stringNeon requires SSL, the platform base works inside the network without it
max: 1 - one connectiondefault pool (up to 10)Page requests via Promise.all occur simultaneously, not in sequence
prepare: falsedefaultIt was only necessary for the Neon puller

4. DNS - via Cloudflare

The recorder .ge had a hidden Vercel record (216.198.79.1, TTL 3600) in the zone, which was not visible in the panel: DNS gave both addresses, and some visitors ended up on the old site. We decided to change A-records to Cloudflare. Records are in “DNS only” mode: Cloudflare only answers what address the domain has, and the traffic goes directly to the server.

A-records in Cloudflare in DNS only modeA-records in Cloudflare in DNS only mode

  • A-records in Cloudflare, “DNS only” mode*

What was accelerated after the move

Immediately after the move, the PR-CY score even dropped - from 87 to 79. The server began to respond faster, but in the browser the page remained heavy: 5.5 MB, of which 4.7 MB were images. Next is work on the page itself.

WhatBeforeAfter
🖼 Pictures in the catalogoriginals 100–450 KB: optimization disabled due to Vercel quotaWebP 256–640 px wide via the Next.js optimizer - it’s free on your server
🎞 Slider in cardthe browser loaded all the photos at once: 20 cards - about 33 picturesthe second and subsequent photos are only taken when the person reaches out to the gallery
📦 Product card data in HTML~6 KB per product: three full descriptions and service fields~1.2 KB: only what the card shows
🏠 Homeentire catalog: 20 cards, 4 photos at once, one of them - 436 KBfirst screen - text and search, sections with lazy photos below
✂️ JS mainsinker catalog code - slider, cards, filters (~100 KB)catalog - separate route, category addresses are the same
🎨 CSStwo files blocked the first rendering (460 ms)one file 8.6 KB: 170 ms only on the first visit, then from the cache on all pages
💬 Support chatin the required JS of each pageloaded after rendering
📊 Google Counters and Metrics (~280 KB JS)loaded along with the pageby the visitor’s first action or after 5 seconds; robots don't load at all
♻️ Page cachelost during cold start of functionslives in the process until the next deployment, updated in the background

Let's be honest about the counters: a person who closed the page in less than 5 seconds and never touched it will not be included in the statistics. This suits us.


Numbers

Service ratings

CheckVercelRelaxDev
PageSpeed ​​Insights, mobiledidn't measure100
PR-CY, download speed87100
PR-CY, site analysisdidn't measure100

Result: 100 out of 100Result: 100 out of 100

  • PageSpeed Insights, mobile - after moving*

PR-CY, speed - before optimizationPR-CY, speed - before optimization

  • PR-CY, download speed - before optimization*

PR-CY, speed - 100PR-CY, speed - 100

  • PR-CY, download speed - after optimization*

PR-CY, site analysis - 100PR-CY, site analysis - 100

  • PR-CY, site analysis*

PageSpeed Insights by Category

Lighthouse 13.5, mobile profile: Moto G Power, slow 4G.

CategoryNow
Productivity100
Accessibility100
Recommendations100
Search Engine Optimization100
Agent View3/3

“Agent viewing” is a new Lighthouse category: how convenient the site is for AI agents. It checks the accessibility tree, layout stability, file llms.txt and WebMCP markup.

PageSpeed Insights - agent review 3/3PageSpeed Insights - agent review 3/3

  • PageSpeed Insights: Agent View - 3 out of 3*

Page load metrics /ru in PR-CY before optimization

MetricVercelRelaxDev, immediately after moving
Evaluation8779
First Contentful Paint1.1 s1.8 sec
Speed ​​Index4.9 sec2.7 sec
Time to Interactive8.4 sec8.5 sec
Total Blocking Time150ms240ms
Cumulative Layout Shift00.01
Largest Contentful Paint—4.4 sec
Server response (TTFB)—110ms

After optimization, PR-CY sets it to 100 - screenshot above. The right column honestly shows: the move itself did not give speed in the browser. The main acceleration comes from pictures, a lightweight main counter, and delayed counters. But on Vercel this was limited by the quota for pictures and the design of the platform.

Page weight

After the move, before optimizationNow
Entire page with all files²5,478 KB, 84 requests723 KB, 58 requests
Pictures on page²4,735 KB, 43 pcs.179 KB, 20 pcs.
HTML main354 KB110 KB
React data inside HTML260 KB57 KB
Styles2 files, rendering blocked for 460 ms1 file, 8.6 KB, further from cache
Product picture, example436 KB (original)44 KB³

² PR-CY, froze before the counters were postponed: now out of these 723 KB, about 280 KB of Google and Metrica scripts are not downloaded on the first download.

³ JPEG size when requested without webp support. Browsers get webp - even easier.

Styles: inside HTML or file

Next can embed CSS directly into the page (experimental.inlineCss). At first, we did just that: a separate request for styles disappeared before the first rendering, and Lighthouse set it to 100. Then we parsed the HTML and saw that Next entered styles three times: 40 KB into the <style> tag and twice more, 40 KB into the React data. Of the 232 KB of main HTML, 121 KB were styles - in every open page.

We returned the styles as a separate file. HTML main became 110 KB instead of 232, React data - 57 KB instead of 139. The style file weighs 8.6 KB, is downloaded once per visit and then taken from the cache on all pages. This benefits visitors who view multiple pages and search robots who crawl all 928 addresses.

Price: on the very first visit, the browser waits for the style file - 170 ms on slow 4G. Because of this, in laboratory measurements the Speed ​​Index increased from 1.3 to 2.4 s. First render and LCP did not deteriorate, and 2.4 s is still in the “green” zone.

Both measurements are PageSpeed Insights, mobile:

MetricStyles inside HTMLStyles as a separate file
First Contentful Paint1.1 s1.0 s
Largest Contentful Paint1.4 s1.3 s
Total Blocking Time20 ms30ms
Cumulative Layout Shift00
Speed ​​Index1.3 s2.4 sec

Server response by moving stages

Globalping, page /ru, three points in each country.

StageTbilisi: first byteTbilisi: full answer ⁴Moscow and St. Petersburg: first byteMoscow and St. Petersburg: the whole answer ⁴
Vercel + Neon1.56–3.54 sec3.5–3.6 sec0.46–0.67 s0.60–0.94 s
RelaxDev, base still in Neon8.5–9.6 sec9.0–10.8 sec0.12–0.13 s0.19–0.23 s
RelaxDev + own base0.16–0.17 s0.36–0.39 s0.05 s0.11–0.13 s
RelaxDev after optimization0.12–0.14 s0.42 s0.02–0.03 s0.12–0.20 s

⁴The “entire answer” includes looking up the address using DNS. In the last measurement, the domain was already on Cloudflare and was resolved again (40–150 ms), in the previous one the address was in the cache (1–30 ms).

Vercel in numbers has a cold start function. But this is exactly what a real visitor who comes in after a pause sees. Measuring points in Moscow data centers reached Vercel, but the site did not open for regular providers and mobile operators.


SEO and AI agents

Since we started focusing on speed, we also went over the technical side.

WasBecame
Addresses in sitemap.xml30928
Addresses of the main page and categories in the sitemapwith a slash, each is a redirectmatch canonical
lang on English and Georgian pagesruen, ka
Tourism subcategory pages08: tents, barbecues, firewood, thermoses...
llms.txt for AI assistantslast year's assortment, links to deleted sectionscollected from the base
The name of the cart iconno - for screen readers and agents “link without name”there is
Search formregularmarked up by WebMCP: browser agents see it as a “product search” tool

Subcategories are a different story. For the queries “tents”, “firewood”, “barbecues”, the store was already in 3rd or 4th place, but there were no pages for these requests: the products did not have a subcategory. We listed them by product names, and eight new pages appeared with links from the main page.


What emerged after the move

The move took evening. The next days were spent on what came up later - perhaps this part is more useful than the numbers.Sitemap for 30 addresses.Next collected the sitemap once, during assembly, but there was no database during assembly. Database errors were caught silently, and only static pages remained in the sitemap - until the next build. Now it is built for each request.Stubs instead of environment variables.When building, the platform substitutes stubs of the form auto-generated-stub-for-build instead of unspecified variables, and they survive until the server starts running. Everything that begins with NEXT_PUBLIC_ is stitched into the code by Next during assembly, so the forgotten public variable ends up in the browser as a stub. Now the stubs are cleared when the server starts, and their list is written to the log: you can immediately see which variables were forgotten to be transferred from Vercel.Notifications in Telegram did not work the first time.After the move, the bot stopped sending orders and responding in the support chat. We decided in the panel: we enabled the webhook receiver and network settings for outgoing requests. One subtlety: the receiver puts a webhook with a secret from the project variables, and the site must check exactly this - at first we had 401 errors.Phone mask that was losing customers.Not about speed, but it came up in the same days. The phone field cut the insert to length and did not remove the country code: “+7 989 156-35-49” turned into “+7 798915635”, and the order did not go through. Now the number can be inserted in any way you like - with +7, with 8, with brackets - and the country is determined by the code itself.The old line prisma db push --accept-data-loss in the relaxdev.ru builder. Prisma has not been used in the code for a long time, but the line remains. While the assembly did not have access to the base, it silently fell. If we gave her access, we would bring the database into compliance with the schema, where there are no blog tables or support chat.


Summary

  • ✅ The site is opening again from Russia.
  • ✅ 100 out of 100 in PageSpeed ​​Insights, in speed and in PR-CY site analysis.
  • ✅ In Georgia, the server responds in 0.12–0.14 s instead of 1.5–3.5, the entire page arrives in 0.42 s instead of 3.5.
  • ✅ The page is 7 times lighter: 723 KB instead of 5.5 MB, the main HTML is 110 KB instead of 354, the pictures in the catalog are 10 times lighter.
  • ✅ No database limits or outdated cards.
  • ✅ Sitemap has 928 addresses instead of 30.
  • ✅ The whole move - one evening: variables with one command, a database based on the connection string, a couple of edits in the code.
  • ✅ We do not count resource limits on Vercel and are sure that the site will be available during the holidays.

Links to measurements:

This is a translation of the original Russian article. Comments are on the Russian page.