Vibecode Vercel
track this build5 phases, 15 steps, beginner friendly0%You can deploy to a VPS or self-host PaaS, but Vercel's edge network, preview deployments, CI/CD, observability, and Next.js integration are the paid value.
You are building a lean indie version of Vercel. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Vercel · indie build Coolify on one server, deploying your application on every push, giving each branch its own preview URL with its own certificate, keeping the previous release ready for an instant rollback, and backing up both the application data and the platform's own configuration off the machine. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Platform | Coolify, self-hosted | the only open option that does per-branch preview deployments through git webhooks, which is the feature people actually miss | | Host | One Linux server with 4 GB of memory, more if you build on it | Coolify idles around 2 GB and a Node build wants headroom, so 2 GB will fail during the build rather than at runtime | | Routing | Traefik, which Coolify manages, with Let's Encrypt | wildcard-capable routing is what makes per-branch preview hostnames possible | | Registry | Local image builds on the same server to start | a remote registry is the upgrade once builds start competing with production for memory | | Backups | The application's own data plus Coolify's configuration, both off the machine | losing the platform configuration means rebuilding every service by hand from memory | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **A Linux server with at least 4 GB of memory** · roughly 10 to 25 USD a month depending on provider and memory - Why: Coolify idles around 2 GB and a build wants the rest. On 2 GB the build kills production, which looks like a mystery outage. - Get it: Any virtual server provider. Choose a region close to your users, because that choice is now your entire content delivery strategy. - Verify: free -m reports 4 GB or more - [ ] **A second server for the restore drill** · a few USD if deleted the same day - Why: The drill proves nothing on the machine that already has the state. Create it for a day and destroy it after. - Get it: The same provider, smallest plan meeting the memory floor. - Verify: You can SSH to it - [ ] **A domain with API access to its DNS** · roughly 10 to 15 USD a year - Why: Wildcard certificates for preview hostnames need a DNS challenge, which needs an API token. A registrar without an API makes previews far harder. - Get it: Use a registrar or DNS host with an API, create a token scoped to the zone, and add both the A record and the wildcard record. - Verify: The wildcard hostname resolves and the token can create a TXT record - [ ] **A git host with webhook support** · free for personal repositories - Why: The whole pipeline is webhook driven. Without it you are deploying by hand, which is the thing you were trying to stop doing. - Get it: GitHub, GitLab or a self-hosted forge. Connect it in the platform and authorise the webhook. - Verify: A test push shows up as a deployment - [ ] **An off-server backup destination** · a few USD a month at these volumes - Why: Backups on the server die with the server. This includes the platform configuration, which exists nowhere else. - Get it: Object storage from any provider, with credentials scoped to write into one bucket. - Verify: A test file uploads and lists from your laptop ## Quick start ```sh ufw allow OpenSSH && ufw allow 80,443/tcp && ufw enable nmap -Pn YOUR_SERVER_IP ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - The global edge network, which is the largest thing you give up. - Automatic scaling under traffic you did not expect. - Framework-specific optimisations and edge functions. - Someone else carrying the pager. - edge/CDN performance - preview deployments - scaling - observability - image functions - team workflows - Next.js integration If one of those is essential to you, that is the reason to keep paying for Vercel, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Vercel The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a git-driven deploy pipeline on your own server, with preview URLs per branch and instant rollback to replace Vercel. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Do not write a platform as a service. Coolify already does this job: git webhooks, per-branch preview deployments, automatic certificates and one-click databases, on a server you rent for the price of a sandwich. This is an operations task. Every phase is deploy, configure and verify, and the last one is a restore drill you actually perform. Be clear with yourself about what you are not getting back. The edge network is the product, and one server in one region is not a substitute for it. What you are buying with this work is control and a predictable bill, not equivalent performance. ### Stack (fixed, do not substitute) - Platform: Coolify, self-hosted · the only open option that does per-branch preview deployments through git webhooks, which is the feature people actually miss - Host: One Linux server with 4 GB of memory, more if you build on it · Coolify idles around 2 GB and a Node build wants headroom, so 2 GB will fail during the build rather than at runtime - Routing: Traefik, which Coolify manages, with Let's Encrypt · wildcard-capable routing is what makes per-branch preview hostnames possible - Registry: Local image builds on the same server to start · a remote registry is the upgrade once builds start competing with production for memory - Backups: The application's own data plus Coolify's configuration, both off the machine · losing the platform configuration means rebuilding every service by hand from memory ### Data model (create this before Phase 1) - This is infrastructure, so the state worth writing down is operational. - server · address, memory, disk, and what else is running on it - services · one per application, with its repository, branch, build command and environment variables - domains · production hostname, and the wildcard used for preview hostnames - secrets · every environment variable per service, kept somewhere that is not the server - backups · application data, Coolify configuration, and the date of the last restore drill ### Phase 1 · Server, DNS and the platform Build: Provision a server with at least 4 GB of memory. Install Coolify. Point an A record at it for the dashboard and a wildcard record for preview hostnames. Close the firewall to everything except SSH and the web ports. Trap: Preview deployments need a wildcard DNS record, and a wildcard needs a wildcard certificate, which needs a DNS challenge rather than the usual HTTP one. That means an API token for your DNS provider. Sort this out in Phase 1, because discovering it when your first preview URL fails to get a certificate is a bad afternoon. Done when: The Coolify dashboard is reachable over HTTPS with a valid certificate and requires a login. A wildcard DNS record resolves to the server. A port scan from another machine shows only the ports you intended. Do not build yet: No applications deployed yet. ### Phase 2 · First deploy from a push Build: Connect the git repository, configure the build, and add the webhook so a push triggers a deploy. Set environment variables in the platform rather than in the repository. Watch the first build log all the way through. Trap: Builds run on the same server as production by default, and a Node build can consume more memory than the running application. On a 4 GB server this shows up as the build killing production, which looks like a random outage. Watch memory during the first build, and add swap or move builds elsewhere before it bites. Done when: A push to the main branch produces a new deployment without manual intervention. The application answers on the production hostname over HTTPS. Memory during the build was observed and stayed within the server's capacity, or swap was added after seeing that it did not. Do not build yet: No previews and no rollback yet. ### Phase 3 · Preview deployments per branch Build: Enable preview deployments so each branch or pull request builds to its own hostname under the wildcard, with its own certificate. Give previews their own environment variables so they never write to production data. Trap: A preview environment that inherits production environment variables will happily write to the production database, and the first time it does you will find out through data rather than through an error. Give previews their own database and their own variables from the very first preview, not after the first accident. Done when: Opening a pull request produces a working URL with a valid certificate within a few minutes. That preview is verified to be writing to a non-production database. Closing the pull request removes the preview and frees its resources. Do not build yet: No rollback or backups yet. ### Phase 4 · Rollback and health Build: Keep previous images so a rollback is immediate. Add a health check so a failed deployment does not replace a working one. Confirm both by deliberately deploying something broken. Trap: A deployment strategy that stops the old container before the new one is healthy gives you downtime on every deploy and an outage on every bad one. Configure the health check and confirm the old container keeps serving until the new one passes. Done when: Deploying a deliberately broken commit leaves the previous version serving, verified by requesting the site during the failed deploy. Rolling back to the previous deployment restores service in under a minute. The health check endpoint touches the database rather than only returning a static response. Do not build yet: Backups come next. ### Phase 5 · Backups, including the platform itself Build: Back up the application's data and Coolify's own configuration, both off the server, both dated. The platform configuration is the part everyone forgets and the part that takes longest to rebuild by hand. Trap: Backing up your database and not the platform configuration means a restore leaves you re-entering every service, domain and environment variable from memory. The environment variables in particular exist nowhere else once the server is gone. Done when: Dated backups of both the application data and the platform configuration land off the server nightly. The environment variables for every service exist somewhere that is not the server. A missing backup produces an alert. Do not build yet: The restore drill is the last phase and it is the one that counts. ### Phase 6 · The restore drill and the honest comparison Build: Provision a second server, install the platform, restore the configuration and the data, and bring the application up. Record the date and the duration. Then compare total cost and performance honestly against the platform you left. Trap: Measure page load from somewhere far away, not from your desk. One server in one region will be markedly slower for distant visitors than an edge network, and this is the single largest thing you gave up. If most of your visitors are far from your server, that difference may outweigh the saving. Done when: A second server serves the application from restored backups, with the same data, and the date and duration are recorded. A latency comparison from at least two distant locations is written down alongside the monthly cost of both options. Do not build yet: Nothing after this. ### Out of scope (and why) - The edge network. This is the product, and one server in one region does not approximate it. Distant visitors will notice. - Automatic scaling under load. Your server has the capacity it has, and a traffic spike is now your problem at three in the morning. - Image optimisation, edge functions and the framework-specific features that only exist because the platform builds them. - Someone else being responsible for uptime. That is now you, and it is the part that costs more than the bill you saved. ### README must contain - The server specification, and that builds and production share its memory - That preview deployments need a wildcard record and a DNS challenge, with the provider token this requires - That preview environments use their own database, and how that isolation was verified - That backups cover both application data and the platform configuration, and where the environment variables live - The date and duration of the last restore drill, and the latency comparison against the platform you left ===== AGENTS.md ===== # Agent instructions · Vercel indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Coolify, self-hosted, One Linux server with 4 GB of memory, more if you build on it, Traefik, which Coolify manages, with Let's Encrypt, Local image builds on the same server to start, The application's own data plus Coolify's configuration, both off the machine. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Preview deployments need a wildcard DNS record, and a wildcard needs a wildcard certificate, which needs a DNS challenge rather than the usual HTTP one. That means an API token for your DNS provider. Sort this out in Phase 1, because discovering it when your first preview URL fails to get a certificate is a bad afternoon. - Builds run on the same server as production by default, and a Node build can consume more memory than the running application. On a 4 GB server this shows up as the build killing production, which looks like a random outage. Watch memory during the first build, and add swap or move builds elsewhere before it bites. - A preview environment that inherits production environment variables will happily write to the production database, and the first time it does you will find out through data rather than through an error. Give previews their own database and their own variables from the very first preview, not after the first accident. - A deployment strategy that stops the old container before the new one is healthy gives you downtime on every deploy and an outage on every bad one. Configure the health check and confirm the old container keeps serving until the new one passes. - Backing up your database and not the platform configuration means a restore leaves you re-entering every service, domain and environment variable from memory. The environment variables in particular exist nowhere else once the server is gone. ===== BUILD_PLAN.md ===== # Build plan · Vercel Coolify on one server, deploying your application on every push, giving each branch its own preview URL with its own certificate, keeping the previous release ready for an instant rollback, and backing up both the application data and the platform's own configuration off the machine. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Server, DNS and the platform Coolify runs, you can reach its dashboard safely, and DNS is ready for preview hostnames. ### Steps 1. Provision and close the server A non-root user, SSH keys only, and a firewall allowing SSH, 80 and 443. Verify with a scan from your laptop rather than by reading the configuration. ```sh ufw allow OpenSSH && ufw allow 80,443/tcp && ufw enable nmap -Pn YOUR_SERVER_IP ``` 2. Install Coolify Its installation script sets up Docker and the platform. Change the initial dashboard credentials immediately. ```sh curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash ``` 3. Set up DNS including the wildcard An A record for the dashboard and for production, plus a wildcard for preview hostnames. Add a DNS provider API token so wildcard certificates can be issued. ```sh dig +short app.example.com dig +short test.preview.example.com ``` ### Done when - [ ] The dashboard loads over HTTPS with a valid certificate and requires login - [ ] A wildcard hostname resolves to the server - [ ] Only the intended ports are open, verified from another machine - [ ] The default dashboard credentials have been changed ### Watch out - Preview deployments need a wildcard DNS record, and a wildcard needs a wildcard certificate, which needs a DNS challenge rather than the usual HTTP one. That means an API token for your DNS provider. Sort this out in Phase 1, because discovering it when your first preview URL fails to get a certificate is a bad afternoon. ## Phase 2 · First deploy from a push Pushing to the main branch deploys, without you touching the server. ### Steps 1. Connect the repository and configure the build Point at the repository and branch. Let the platform detect the build, then check what it detected rather than assuming it guessed right. 2. Move environment variables into the platform Every secret goes in the platform's own storage, never into the repository. This is also what makes preview environments possible later. 3. Watch memory during the first build Run top on the server while the build runs. This one observation prevents the most common self-hosting outage. ```sh free -m top -o %MEM ``` ### Done when - [ ] A push to main triggers a deploy with no manual step - [ ] The application answers on the production hostname over HTTPS - [ ] No secret is present in the repository - [ ] Build memory was observed and the server coped, or swap was added ### Watch out - Builds run on the same server as production by default, and a Node build can consume more memory than the running application. On a 4 GB server this shows up as the build killing production, which looks like a random outage. Watch memory during the first build, and add swap or move builds elsewhere before it bites. ## Phase 3 · Preview deployments per branch Every pull request gets its own URL, which is the feature you were actually paying for. ### Steps 1. Turn on preview deployments Configure the wildcard domain for previews and confirm certificate issuance works through the DNS challenge. 2. Give previews their own data A separate database, seeded rather than copied from production. Override the connection variable at the preview level. 3. Prove the isolation Write a record from a preview and confirm it is absent from production. Do this once, deliberately, before you trust it. ### Done when - [ ] A pull request produces a working preview URL with a valid certificate - [ ] A write from a preview does not appear in the production database - [ ] Closing the pull request removes the preview - [ ] Preview environment variables differ from production ### Watch out - A preview environment that inherits production environment variables will happily write to the production database, and the first time it does you will find out through data rather than through an error. Give previews their own database and their own variables from the very first preview, not after the first accident. ## Phase 4 · Rollback and health A bad deploy is a one-click problem rather than an emergency. ### Steps 1. Add a real health check An endpoint that queries the database. A static endpoint returns healthy while the application is unable to serve anyone, which is worse than no check. Files: `src/health` 2. Configure the deployment to wait for health The old container keeps serving until the new one passes. Verify by watching requests during a deploy. 3. Break it on purpose and roll back Push a commit that fails to start. Confirm the site stays up, then roll back and time it. ### Done when - [ ] A broken deploy leaves the previous version serving - [ ] Rollback restores service in under a minute - [ ] The health check fails when the database is unreachable - [ ] Requests during a successful deploy are not dropped ### Watch out - A deployment strategy that stops the old container before the new one is healthy gives you downtime on every deploy and an outage on every bad one. Configure the health check and confirm the old container keeps serving until the new one passes. ## Phase 5 · Backups, including the platform itself You can rebuild everything, including the configuration that describes everything. ### Steps 1. Back up application data Database dumps and any uploaded files, dated, off the machine, several generations retained. Files: `scripts/backup.sh` 2. Back up the platform configuration Coolify's own database and configuration directory. This is what stores your services, domains and variables. Files: `scripts/backup.sh` 3. Alert on a missing backup Check each morning that today's files exist. Silence is not evidence that a backup ran. Files: `scripts/check_backup.sh` ### Done when - [ ] Both backup artefacts land off the server nightly - [ ] Every service's environment variables exist outside the server - [ ] A suppressed backup job produces an alert - [ ] Several generations are retained rather than one overwritten file ### Watch out - Backing up your database and not the platform configuration means a restore leaves you re-entering every service, domain and environment variable from memory. The environment variables in particular exist nowhere else once the server is gone. ## Not in this build - The global edge network, which is the largest thing you give up. - Automatic scaling under traffic you did not expect. - Framework-specific optimisations and edge functions. - Someone else carrying the pager. ## After v1, if you want it - Moving builds to a separate machine once they compete with production - A staging environment restored nightly from production backups, which also exercises the restore - A content delivery network in front for static assets, which recovers some of the edge benefit cheaply ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Hostname for the platform dashboard. Keep it off your production domain so an application misconfiguration cannot shadow it. COOLIFY_DOMAIN=deploy.example.com # Required. The wildcard used for per-branch preview hostnames. PREVIEW_WILDCARD_DOMAIN=*.preview.example.com # Required · secret. Your DNS provider's API token, scoped to the zone. Needed for wildcard certificate issuance. DNS_API_TOKEN=replace-with-provider-token # Required · secret. Set in the platform, never in the repository. PRODUCTION_DATABASE_URL=postgres://user:pass@db:5432/app # Required · secret. A different database from production. This variable is the isolation. PREVIEW_DATABASE_URL=postgres://user:pass@db:5432/app_preview # Required. Where both nightly artefacts go: application data and platform configuration. BACKUP_DEST=s3://your-bucket/deploy
You are building a lean indie version of Vercel. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Vercel · indie build Coolify on one server, deploying your application on every push, giving each branch its own preview URL with its own certificate, keeping the previous release ready for an instant rollback, and backing up both the application data and the platform's own configuration off the machine. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Platform | Coolify, self-hosted | the only open option that does per-branch preview deployments through git webhooks, which is the feature people actually miss | | Host | One Linux server with 4 GB of memory, more if you build on it | Coolify idles around 2 GB and a Node build wants headroom, so 2 GB will fail during the build rather than at runtime | | Routing | Traefik, which Coolify manages, with Let's Encrypt | wildcard-capable routing is what makes per-branch preview hostnames possible | | Registry | Local image builds on the same server to start | a remote registry is the upgrade once builds start competing with production for memory | | Backups | The application's own data plus Coolify's configuration, both off the machine | losing the platform configuration means rebuilding every service by hand from memory | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **A Linux server with at least 4 GB of memory** · roughly 10 to 25 USD a month depending on provider and memory - Why: Coolify idles around 2 GB and a build wants the rest. On 2 GB the build kills production, which looks like a mystery outage. - Get it: Any virtual server provider. Choose a region close to your users, because that choice is now your entire content delivery strategy. - Verify: free -m reports 4 GB or more - [ ] **A second server for the restore drill** · a few USD if deleted the same day - Why: The drill proves nothing on the machine that already has the state. Create it for a day and destroy it after. - Get it: The same provider, smallest plan meeting the memory floor. - Verify: You can SSH to it - [ ] **A domain with API access to its DNS** · roughly 10 to 15 USD a year - Why: Wildcard certificates for preview hostnames need a DNS challenge, which needs an API token. A registrar without an API makes previews far harder. - Get it: Use a registrar or DNS host with an API, create a token scoped to the zone, and add both the A record and the wildcard record. - Verify: The wildcard hostname resolves and the token can create a TXT record - [ ] **A git host with webhook support** · free for personal repositories - Why: The whole pipeline is webhook driven. Without it you are deploying by hand, which is the thing you were trying to stop doing. - Get it: GitHub, GitLab or a self-hosted forge. Connect it in the platform and authorise the webhook. - Verify: A test push shows up as a deployment - [ ] **An off-server backup destination** · a few USD a month at these volumes - Why: Backups on the server die with the server. This includes the platform configuration, which exists nowhere else. - Get it: Object storage from any provider, with credentials scoped to write into one bucket. - Verify: A test file uploads and lists from your laptop ## Quick start ```sh ufw allow OpenSSH && ufw allow 80,443/tcp && ufw enable nmap -Pn YOUR_SERVER_IP ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - The global edge network, which is the largest thing you give up. - Automatic scaling under traffic you did not expect. - Framework-specific optimisations and edge functions. - Someone else carrying the pager. - edge/CDN performance - preview deployments - scaling - observability - image functions - team workflows - Next.js integration If one of those is essential to you, that is the reason to keep paying for Vercel, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Vercel The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a git-driven deploy pipeline on your own server, with preview URLs per branch and instant rollback to replace Vercel. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Do not write a platform as a service. Coolify already does this job: git webhooks, per-branch preview deployments, automatic certificates and one-click databases, on a server you rent for the price of a sandwich. This is an operations task. Every phase is deploy, configure and verify, and the last one is a restore drill you actually perform. Be clear with yourself about what you are not getting back. The edge network is the product, and one server in one region is not a substitute for it. What you are buying with this work is control and a predictable bill, not equivalent performance. ### Stack (fixed, do not substitute) - Platform: Coolify, self-hosted · the only open option that does per-branch preview deployments through git webhooks, which is the feature people actually miss - Host: One Linux server with 4 GB of memory, more if you build on it · Coolify idles around 2 GB and a Node build wants headroom, so 2 GB will fail during the build rather than at runtime - Routing: Traefik, which Coolify manages, with Let's Encrypt · wildcard-capable routing is what makes per-branch preview hostnames possible - Registry: Local image builds on the same server to start · a remote registry is the upgrade once builds start competing with production for memory - Backups: The application's own data plus Coolify's configuration, both off the machine · losing the platform configuration means rebuilding every service by hand from memory ### Data model (create this before Phase 1) - This is infrastructure, so the state worth writing down is operational. - server · address, memory, disk, and what else is running on it - services · one per application, with its repository, branch, build command and environment variables - domains · production hostname, and the wildcard used for preview hostnames - secrets · every environment variable per service, kept somewhere that is not the server - backups · application data, Coolify configuration, and the date of the last restore drill ### Phase 1 · Server, DNS and the platform Build: Provision a server with at least 4 GB of memory. Install Coolify. Point an A record at it for the dashboard and a wildcard record for preview hostnames. Close the firewall to everything except SSH and the web ports. Trap: Preview deployments need a wildcard DNS record, and a wildcard needs a wildcard certificate, which needs a DNS challenge rather than the usual HTTP one. That means an API token for your DNS provider. Sort this out in Phase 1, because discovering it when your first preview URL fails to get a certificate is a bad afternoon. Done when: The Coolify dashboard is reachable over HTTPS with a valid certificate and requires a login. A wildcard DNS record resolves to the server. A port scan from another machine shows only the ports you intended. Do not build yet: No applications deployed yet. ### Phase 2 · First deploy from a push Build: Connect the git repository, configure the build, and add the webhook so a push triggers a deploy. Set environment variables in the platform rather than in the repository. Watch the first build log all the way through. Trap: Builds run on the same server as production by default, and a Node build can consume more memory than the running application. On a 4 GB server this shows up as the build killing production, which looks like a random outage. Watch memory during the first build, and add swap or move builds elsewhere before it bites. Done when: A push to the main branch produces a new deployment without manual intervention. The application answers on the production hostname over HTTPS. Memory during the build was observed and stayed within the server's capacity, or swap was added after seeing that it did not. Do not build yet: No previews and no rollback yet. ### Phase 3 · Preview deployments per branch Build: Enable preview deployments so each branch or pull request builds to its own hostname under the wildcard, with its own certificate. Give previews their own environment variables so they never write to production data. Trap: A preview environment that inherits production environment variables will happily write to the production database, and the first time it does you will find out through data rather than through an error. Give previews their own database and their own variables from the very first preview, not after the first accident. Done when: Opening a pull request produces a working URL with a valid certificate within a few minutes. That preview is verified to be writing to a non-production database. Closing the pull request removes the preview and frees its resources. Do not build yet: No rollback or backups yet. ### Phase 4 · Rollback and health Build: Keep previous images so a rollback is immediate. Add a health check so a failed deployment does not replace a working one. Confirm both by deliberately deploying something broken. Trap: A deployment strategy that stops the old container before the new one is healthy gives you downtime on every deploy and an outage on every bad one. Configure the health check and confirm the old container keeps serving until the new one passes. Done when: Deploying a deliberately broken commit leaves the previous version serving, verified by requesting the site during the failed deploy. Rolling back to the previous deployment restores service in under a minute. The health check endpoint touches the database rather than only returning a static response. Do not build yet: Backups come next. ### Phase 5 · Backups, including the platform itself Build: Back up the application's data and Coolify's own configuration, both off the server, both dated. The platform configuration is the part everyone forgets and the part that takes longest to rebuild by hand. Trap: Backing up your database and not the platform configuration means a restore leaves you re-entering every service, domain and environment variable from memory. The environment variables in particular exist nowhere else once the server is gone. Done when: Dated backups of both the application data and the platform configuration land off the server nightly. The environment variables for every service exist somewhere that is not the server. A missing backup produces an alert. Do not build yet: The restore drill is the last phase and it is the one that counts. ### Phase 6 · The restore drill and the honest comparison Build: Provision a second server, install the platform, restore the configuration and the data, and bring the application up. Record the date and the duration. Then compare total cost and performance honestly against the platform you left. Trap: Measure page load from somewhere far away, not from your desk. One server in one region will be markedly slower for distant visitors than an edge network, and this is the single largest thing you gave up. If most of your visitors are far from your server, that difference may outweigh the saving. Done when: A second server serves the application from restored backups, with the same data, and the date and duration are recorded. A latency comparison from at least two distant locations is written down alongside the monthly cost of both options. Do not build yet: Nothing after this. ### Out of scope (and why) - The edge network. This is the product, and one server in one region does not approximate it. Distant visitors will notice. - Automatic scaling under load. Your server has the capacity it has, and a traffic spike is now your problem at three in the morning. - Image optimisation, edge functions and the framework-specific features that only exist because the platform builds them. - Someone else being responsible for uptime. That is now you, and it is the part that costs more than the bill you saved. ### README must contain - The server specification, and that builds and production share its memory - That preview deployments need a wildcard record and a DNS challenge, with the provider token this requires - That preview environments use their own database, and how that isolation was verified - That backups cover both application data and the platform configuration, and where the environment variables live - The date and duration of the last restore drill, and the latency comparison against the platform you left ===== AGENTS.md ===== # Agent instructions · Vercel indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Coolify, self-hosted, One Linux server with 4 GB of memory, more if you build on it, Traefik, which Coolify manages, with Let's Encrypt, Local image builds on the same server to start, The application's own data plus Coolify's configuration, both off the machine. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Preview deployments need a wildcard DNS record, and a wildcard needs a wildcard certificate, which needs a DNS challenge rather than the usual HTTP one. That means an API token for your DNS provider. Sort this out in Phase 1, because discovering it when your first preview URL fails to get a certificate is a bad afternoon. - Builds run on the same server as production by default, and a Node build can consume more memory than the running application. On a 4 GB server this shows up as the build killing production, which looks like a random outage. Watch memory during the first build, and add swap or move builds elsewhere before it bites. - A preview environment that inherits production environment variables will happily write to the production database, and the first time it does you will find out through data rather than through an error. Give previews their own database and their own variables from the very first preview, not after the first accident. - A deployment strategy that stops the old container before the new one is healthy gives you downtime on every deploy and an outage on every bad one. Configure the health check and confirm the old container keeps serving until the new one passes. - Backing up your database and not the platform configuration means a restore leaves you re-entering every service, domain and environment variable from memory. The environment variables in particular exist nowhere else once the server is gone. ===== BUILD_PLAN.md ===== # Build plan · Vercel Coolify on one server, deploying your application on every push, giving each branch its own preview URL with its own certificate, keeping the previous release ready for an instant rollback, and backing up both the application data and the platform's own configuration off the machine. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Server, DNS and the platform Coolify runs, you can reach its dashboard safely, and DNS is ready for preview hostnames. ### Steps 1. Provision and close the server A non-root user, SSH keys only, and a firewall allowing SSH, 80 and 443. Verify with a scan from your laptop rather than by reading the configuration. ```sh ufw allow OpenSSH && ufw allow 80,443/tcp && ufw enable nmap -Pn YOUR_SERVER_IP ``` 2. Install Coolify Its installation script sets up Docker and the platform. Change the initial dashboard credentials immediately. ```sh curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash ``` 3. Set up DNS including the wildcard An A record for the dashboard and for production, plus a wildcard for preview hostnames. Add a DNS provider API token so wildcard certificates can be issued. ```sh dig +short app.example.com dig +short test.preview.example.com ``` ### Done when - [ ] The dashboard loads over HTTPS with a valid certificate and requires login - [ ] A wildcard hostname resolves to the server - [ ] Only the intended ports are open, verified from another machine - [ ] The default dashboard credentials have been changed ### Watch out - Preview deployments need a wildcard DNS record, and a wildcard needs a wildcard certificate, which needs a DNS challenge rather than the usual HTTP one. That means an API token for your DNS provider. Sort this out in Phase 1, because discovering it when your first preview URL fails to get a certificate is a bad afternoon. ## Phase 2 · First deploy from a push Pushing to the main branch deploys, without you touching the server. ### Steps 1. Connect the repository and configure the build Point at the repository and branch. Let the platform detect the build, then check what it detected rather than assuming it guessed right. 2. Move environment variables into the platform Every secret goes in the platform's own storage, never into the repository. This is also what makes preview environments possible later. 3. Watch memory during the first build Run top on the server while the build runs. This one observation prevents the most common self-hosting outage. ```sh free -m top -o %MEM ``` ### Done when - [ ] A push to main triggers a deploy with no manual step - [ ] The application answers on the production hostname over HTTPS - [ ] No secret is present in the repository - [ ] Build memory was observed and the server coped, or swap was added ### Watch out - Builds run on the same server as production by default, and a Node build can consume more memory than the running application. On a 4 GB server this shows up as the build killing production, which looks like a random outage. Watch memory during the first build, and add swap or move builds elsewhere before it bites. ## Phase 3 · Preview deployments per branch Every pull request gets its own URL, which is the feature you were actually paying for. ### Steps 1. Turn on preview deployments Configure the wildcard domain for previews and confirm certificate issuance works through the DNS challenge. 2. Give previews their own data A separate database, seeded rather than copied from production. Override the connection variable at the preview level. 3. Prove the isolation Write a record from a preview and confirm it is absent from production. Do this once, deliberately, before you trust it. ### Done when - [ ] A pull request produces a working preview URL with a valid certificate - [ ] A write from a preview does not appear in the production database - [ ] Closing the pull request removes the preview - [ ] Preview environment variables differ from production ### Watch out - A preview environment that inherits production environment variables will happily write to the production database, and the first time it does you will find out through data rather than through an error. Give previews their own database and their own variables from the very first preview, not after the first accident. ## Phase 4 · Rollback and health A bad deploy is a one-click problem rather than an emergency. ### Steps 1. Add a real health check An endpoint that queries the database. A static endpoint returns healthy while the application is unable to serve anyone, which is worse than no check. Files: `src/health` 2. Configure the deployment to wait for health The old container keeps serving until the new one passes. Verify by watching requests during a deploy. 3. Break it on purpose and roll back Push a commit that fails to start. Confirm the site stays up, then roll back and time it. ### Done when - [ ] A broken deploy leaves the previous version serving - [ ] Rollback restores service in under a minute - [ ] The health check fails when the database is unreachable - [ ] Requests during a successful deploy are not dropped ### Watch out - A deployment strategy that stops the old container before the new one is healthy gives you downtime on every deploy and an outage on every bad one. Configure the health check and confirm the old container keeps serving until the new one passes. ## Phase 5 · Backups, including the platform itself You can rebuild everything, including the configuration that describes everything. ### Steps 1. Back up application data Database dumps and any uploaded files, dated, off the machine, several generations retained. Files: `scripts/backup.sh` 2. Back up the platform configuration Coolify's own database and configuration directory. This is what stores your services, domains and variables. Files: `scripts/backup.sh` 3. Alert on a missing backup Check each morning that today's files exist. Silence is not evidence that a backup ran. Files: `scripts/check_backup.sh` ### Done when - [ ] Both backup artefacts land off the server nightly - [ ] Every service's environment variables exist outside the server - [ ] A suppressed backup job produces an alert - [ ] Several generations are retained rather than one overwritten file ### Watch out - Backing up your database and not the platform configuration means a restore leaves you re-entering every service, domain and environment variable from memory. The environment variables in particular exist nowhere else once the server is gone. ## Not in this build - The global edge network, which is the largest thing you give up. - Automatic scaling under traffic you did not expect. - Framework-specific optimisations and edge functions. - Someone else carrying the pager. ## After v1, if you want it - Moving builds to a separate machine once they compete with production - A staging environment restored nightly from production backups, which also exercises the restore - A content delivery network in front for static assets, which recovers some of the edge benefit cheaply ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Hostname for the platform dashboard. Keep it off your production domain so an application misconfiguration cannot shadow it. COOLIFY_DOMAIN=deploy.example.com # Required. The wildcard used for per-branch preview hostnames. PREVIEW_WILDCARD_DOMAIN=*.preview.example.com # Required · secret. Your DNS provider's API token, scoped to the zone. Needed for wildcard certificate issuance. DNS_API_TOKEN=replace-with-provider-token # Required · secret. Set in the platform, never in the repository. PRODUCTION_DATABASE_URL=postgres://user:pass@db:5432/app # Required · secret. A different database from production. This variable is the isolation. PREVIEW_DATABASE_URL=postgres://user:pass@db:5432/app_preview # Required. Where both nightly artefacts go: application data and platform configuration. BACKUP_DEST=s3://your-bucket/deploy
You are building a production product version of Vercel. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== PRODUCT.md ===== # Vercel · product brief ## Problem You can deploy to a VPS or self-host PaaS, but Vercel's edge network, preview deployments, CI/CD, observability, and Next.js integration are the paid value. ## Product outcome A deploy pipeline that triggers on push, gives every branch an isolated preview URL, rolls back in under a minute, backs up both the data and the configuration that describes it, and whose real cost and latency you have measured rather than assumed. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - VPS/cloud account - domain/DNS - CI/CD - reverse proxy - SSL - monitoring - backups ## Explicit non-goals for v1 - The global edge network, which is the largest thing you give up. - Automatic scaling under traffic you did not expect. - Framework-specific optimisations and edge functions. - Someone else carrying the pager. - edge/CDN performance - preview deployments - scaling - observability - image functions - team workflows - Next.js integration ## Success criteria - A broken deploy is proved to leave the previous version serving - Preview isolation from production data is verified by an actual write - Both backup artefacts exist off the server and are dated - A restore has been completed on a second server and dated - Latency from distant locations has been measured and recorded ===== BRIEF.md ===== # Build brief · Vercel The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a git-driven deploy pipeline on your own server, with preview URLs per branch and instant rollback to replace Vercel. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Do not write a platform as a service. Coolify already does this job: git webhooks, per-branch preview deployments, automatic certificates and one-click databases, on a server you rent for the price of a sandwich. This is an operations task. Every phase is deploy, configure and verify, and the last one is a restore drill you actually perform. Be clear with yourself about what you are not getting back. The edge network is the product, and one server in one region is not a substitute for it. What you are buying with this work is control and a predictable bill, not equivalent performance. ### Stack (fixed, do not substitute) - Platform: Coolify, self-hosted · the only open option that does per-branch preview deployments through git webhooks, which is the feature people actually miss - Host: One Linux server with 4 GB of memory, more if you build on it · Coolify idles around 2 GB and a Node build wants headroom, so 2 GB will fail during the build rather than at runtime - Routing: Traefik, which Coolify manages, with Let's Encrypt · wildcard-capable routing is what makes per-branch preview hostnames possible - Registry: Local image builds on the same server to start · a remote registry is the upgrade once builds start competing with production for memory - Backups: The application's own data plus Coolify's configuration, both off the machine · losing the platform configuration means rebuilding every service by hand from memory ### Data model (create this before Phase 1) - This is infrastructure, so the state worth writing down is operational. - server · address, memory, disk, and what else is running on it - services · one per application, with its repository, branch, build command and environment variables - domains · production hostname, and the wildcard used for preview hostnames - secrets · every environment variable per service, kept somewhere that is not the server - backups · application data, Coolify configuration, and the date of the last restore drill ### Phase 1 · Server, DNS and the platform Build: Provision a server with at least 4 GB of memory. Install Coolify. Point an A record at it for the dashboard and a wildcard record for preview hostnames. Close the firewall to everything except SSH and the web ports. Trap: Preview deployments need a wildcard DNS record, and a wildcard needs a wildcard certificate, which needs a DNS challenge rather than the usual HTTP one. That means an API token for your DNS provider. Sort this out in Phase 1, because discovering it when your first preview URL fails to get a certificate is a bad afternoon. Done when: The Coolify dashboard is reachable over HTTPS with a valid certificate and requires a login. A wildcard DNS record resolves to the server. A port scan from another machine shows only the ports you intended. Do not build yet: No applications deployed yet. ### Phase 2 · First deploy from a push Build: Connect the git repository, configure the build, and add the webhook so a push triggers a deploy. Set environment variables in the platform rather than in the repository. Watch the first build log all the way through. Trap: Builds run on the same server as production by default, and a Node build can consume more memory than the running application. On a 4 GB server this shows up as the build killing production, which looks like a random outage. Watch memory during the first build, and add swap or move builds elsewhere before it bites. Done when: A push to the main branch produces a new deployment without manual intervention. The application answers on the production hostname over HTTPS. Memory during the build was observed and stayed within the server's capacity, or swap was added after seeing that it did not. Do not build yet: No previews and no rollback yet. ### Phase 3 · Preview deployments per branch Build: Enable preview deployments so each branch or pull request builds to its own hostname under the wildcard, with its own certificate. Give previews their own environment variables so they never write to production data. Trap: A preview environment that inherits production environment variables will happily write to the production database, and the first time it does you will find out through data rather than through an error. Give previews their own database and their own variables from the very first preview, not after the first accident. Done when: Opening a pull request produces a working URL with a valid certificate within a few minutes. That preview is verified to be writing to a non-production database. Closing the pull request removes the preview and frees its resources. Do not build yet: No rollback or backups yet. ### Phase 4 · Rollback and health Build: Keep previous images so a rollback is immediate. Add a health check so a failed deployment does not replace a working one. Confirm both by deliberately deploying something broken. Trap: A deployment strategy that stops the old container before the new one is healthy gives you downtime on every deploy and an outage on every bad one. Configure the health check and confirm the old container keeps serving until the new one passes. Done when: Deploying a deliberately broken commit leaves the previous version serving, verified by requesting the site during the failed deploy. Rolling back to the previous deployment restores service in under a minute. The health check endpoint touches the database rather than only returning a static response. Do not build yet: Backups come next. ### Phase 5 · Backups, including the platform itself Build: Back up the application's data and Coolify's own configuration, both off the server, both dated. The platform configuration is the part everyone forgets and the part that takes longest to rebuild by hand. Trap: Backing up your database and not the platform configuration means a restore leaves you re-entering every service, domain and environment variable from memory. The environment variables in particular exist nowhere else once the server is gone. Done when: Dated backups of both the application data and the platform configuration land off the server nightly. The environment variables for every service exist somewhere that is not the server. A missing backup produces an alert. Do not build yet: The restore drill is the last phase and it is the one that counts. ### Phase 6 · The restore drill and the honest comparison Build: Provision a second server, install the platform, restore the configuration and the data, and bring the application up. Record the date and the duration. Then compare total cost and performance honestly against the platform you left. Trap: Measure page load from somewhere far away, not from your desk. One server in one region will be markedly slower for distant visitors than an edge network, and this is the single largest thing you gave up. If most of your visitors are far from your server, that difference may outweigh the saving. Done when: A second server serves the application from restored backups, with the same data, and the date and duration are recorded. A latency comparison from at least two distant locations is written down alongside the monthly cost of both options. Do not build yet: Nothing after this. ### Out of scope (and why) - The edge network. This is the product, and one server in one region does not approximate it. Distant visitors will notice. - Automatic scaling under load. Your server has the capacity it has, and a traffic spike is now your problem at three in the morning. - Image optimisation, edge functions and the framework-specific features that only exist because the platform builds them. - Someone else being responsible for uptime. That is now you, and it is the part that costs more than the bill you saved. ### README must contain - The server specification, and that builds and production share its memory - That preview deployments need a wildcard record and a DNS challenge, with the provider token this requires - That preview environments use their own database, and how that isolation was verified - That backups cover both application data and the platform configuration, and where the environment variables live - The date and duration of the last restore drill, and the latency comparison against the platform you left ===== ARCHITECTURE.md ===== # Architecture · Vercel ## Stack | Part | Choice | Why | | --- | --- | --- | | Platform | Coolify, self-hosted | the only open option that does per-branch preview deployments through git webhooks, which is the feature people actually miss | | Host | One Linux server with 4 GB of memory, more if you build on it | Coolify idles around 2 GB and a Node build wants headroom, so 2 GB will fail during the build rather than at runtime | | Routing | Traefik, which Coolify manages, with Let's Encrypt | wildcard-capable routing is what makes per-branch preview hostnames possible | | Registry | Local image builds on the same server to start | a remote registry is the upgrade once builds start competing with production for memory | | Backups | The application's own data plus Coolify's configuration, both off the machine | losing the platform configuration means rebuilding every service by hand from memory | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Platform | Builds, deployments, routing and certificates. | Dokploy is lighter and lacks preview environments, which is the feature this whole build exists for. Swapping means giving that up. | | Routing | Hostnames, TLS and preview wildcards. | Managed by the platform. Replacing it means leaving the platform, not reconfiguring it. | | Build | Turning a commit into a runnable image. | Move builds to a separate machine or a hosted runner as soon as they compete with production for memory. This is the first thing to outgrow one server. | | Data | Application databases and uploads, per environment. | Preview and production isolation must survive any change here. It is the invariant, not the implementation. | | Backups | Data and platform configuration, off the machine. | Destination is replaceable. Including the platform configuration is not optional. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `COOLIFY_DOMAIN` · required · Hostname for the platform dashboard. Keep it off your production domain so an application misconfiguration cannot shadow it. - `PREVIEW_WILDCARD_DOMAIN` · required · The wildcard used for per-branch preview hostnames. - `DNS_API_TOKEN` · required, secret · Your DNS provider's API token, scoped to the zone. Needed for wildcard certificate issuance. - `PRODUCTION_DATABASE_URL` · required, secret · Set in the platform, never in the repository. - `PREVIEW_DATABASE_URL` · required, secret · A different database from production. This variable is the isolation. - `BACKUP_DEST` · required · Where both nightly artefacts go: application data and platform configuration. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it. ===== AGENTS.md ===== # Agent instructions · Vercel product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Coolify, self-hosted, One Linux server with 4 GB of memory, more if you build on it, Traefik, which Coolify manages, with Let's Encrypt, Local image builds on the same server to start, The application's own data plus Coolify's configuration, both off the machine. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Preview deployments need a wildcard DNS record, and a wildcard needs a wildcard certificate, which needs a DNS challenge rather than the usual HTTP one. That means an API token for your DNS provider. Sort this out in Phase 1, because discovering it when your first preview URL fails to get a certificate is a bad afternoon. - Builds run on the same server as production by default, and a Node build can consume more memory than the running application. On a 4 GB server this shows up as the build killing production, which looks like a random outage. Watch memory during the first build, and add swap or move builds elsewhere before it bites. - A preview environment that inherits production environment variables will happily write to the production database, and the first time it does you will find out through data rather than through an error. Give previews their own database and their own variables from the very first preview, not after the first accident. - A deployment strategy that stops the old container before the new one is healthy gives you downtime on every deploy and an outage on every bad one. Configure the health check and confirm the old container keeps serving until the new one passes. - Backing up your database and not the platform configuration means a restore leaves you re-entering every service, domain and environment variable from memory. The environment variables in particular exist nowhere else once the server is gone. - Measure page load from somewhere far away, not from your desk. One server in one region will be markedly slower for distant visitors than an edge network, and this is the single largest thing you gave up. If most of your visitors are far from your server, that difference may outweigh the saving. ===== MILESTONES.md ===== # Delivery milestones · Vercel Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Server, DNS and the platform Coolify runs, you can reach its dashboard safely, and DNS is ready for preview hostnames. ### Steps 1. Provision and close the server A non-root user, SSH keys only, and a firewall allowing SSH, 80 and 443. Verify with a scan from your laptop rather than by reading the configuration. ```sh ufw allow OpenSSH && ufw allow 80,443/tcp && ufw enable nmap -Pn YOUR_SERVER_IP ``` 2. Install Coolify Its installation script sets up Docker and the platform. Change the initial dashboard credentials immediately. ```sh curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash ``` 3. Set up DNS including the wildcard An A record for the dashboard and for production, plus a wildcard for preview hostnames. Add a DNS provider API token so wildcard certificates can be issued. ```sh dig +short app.example.com dig +short test.preview.example.com ``` ### Done when - [ ] The dashboard loads over HTTPS with a valid certificate and requires login - [ ] A wildcard hostname resolves to the server - [ ] Only the intended ports are open, verified from another machine - [ ] The default dashboard credentials have been changed ### Watch out - Preview deployments need a wildcard DNS record, and a wildcard needs a wildcard certificate, which needs a DNS challenge rather than the usual HTTP one. That means an API token for your DNS provider. Sort this out in Phase 1, because discovering it when your first preview URL fails to get a certificate is a bad afternoon. ## M2 · First deploy from a push Pushing to the main branch deploys, without you touching the server. ### Steps 1. Connect the repository and configure the build Point at the repository and branch. Let the platform detect the build, then check what it detected rather than assuming it guessed right. 2. Move environment variables into the platform Every secret goes in the platform's own storage, never into the repository. This is also what makes preview environments possible later. 3. Watch memory during the first build Run top on the server while the build runs. This one observation prevents the most common self-hosting outage. ```sh free -m top -o %MEM ``` ### Done when - [ ] A push to main triggers a deploy with no manual step - [ ] The application answers on the production hostname over HTTPS - [ ] No secret is present in the repository - [ ] Build memory was observed and the server coped, or swap was added ### Watch out - Builds run on the same server as production by default, and a Node build can consume more memory than the running application. On a 4 GB server this shows up as the build killing production, which looks like a random outage. Watch memory during the first build, and add swap or move builds elsewhere before it bites. ## M3 · Preview deployments per branch Every pull request gets its own URL, which is the feature you were actually paying for. ### Steps 1. Turn on preview deployments Configure the wildcard domain for previews and confirm certificate issuance works through the DNS challenge. 2. Give previews their own data A separate database, seeded rather than copied from production. Override the connection variable at the preview level. 3. Prove the isolation Write a record from a preview and confirm it is absent from production. Do this once, deliberately, before you trust it. ### Done when - [ ] A pull request produces a working preview URL with a valid certificate - [ ] A write from a preview does not appear in the production database - [ ] Closing the pull request removes the preview - [ ] Preview environment variables differ from production ### Watch out - A preview environment that inherits production environment variables will happily write to the production database, and the first time it does you will find out through data rather than through an error. Give previews their own database and their own variables from the very first preview, not after the first accident. ## M4 · Rollback and health A bad deploy is a one-click problem rather than an emergency. ### Steps 1. Add a real health check An endpoint that queries the database. A static endpoint returns healthy while the application is unable to serve anyone, which is worse than no check. Files: `src/health` 2. Configure the deployment to wait for health The old container keeps serving until the new one passes. Verify by watching requests during a deploy. 3. Break it on purpose and roll back Push a commit that fails to start. Confirm the site stays up, then roll back and time it. ### Done when - [ ] A broken deploy leaves the previous version serving - [ ] Rollback restores service in under a minute - [ ] The health check fails when the database is unreachable - [ ] Requests during a successful deploy are not dropped ### Watch out - A deployment strategy that stops the old container before the new one is healthy gives you downtime on every deploy and an outage on every bad one. Configure the health check and confirm the old container keeps serving until the new one passes. ## M5 · Backups, including the platform itself You can rebuild everything, including the configuration that describes everything. ### Steps 1. Back up application data Database dumps and any uploaded files, dated, off the machine, several generations retained. Files: `scripts/backup.sh` 2. Back up the platform configuration Coolify's own database and configuration directory. This is what stores your services, domains and variables. Files: `scripts/backup.sh` 3. Alert on a missing backup Check each morning that today's files exist. Silence is not evidence that a backup ran. Files: `scripts/check_backup.sh` ### Done when - [ ] Both backup artefacts land off the server nightly - [ ] Every service's environment variables exist outside the server - [ ] A suppressed backup job produces an alert - [ ] Several generations are retained rather than one overwritten file ### Watch out - Backing up your database and not the platform configuration means a restore leaves you re-entering every service, domain and environment variable from memory. The environment variables in particular exist nowhere else once the server is gone. ## M6 · The restore drill and the honest comparison (production only) You have rebuilt it on a new server, and you know what this actually costs. ### Steps 1. Restore onto a clean server New server, fresh platform install, restore the configuration, restore the data, point DNS at it temporarily and verify. 2. Record the date and duration In the README. A restore you did once and dated is worth more than a runbook you have never followed. Files: `README.md` 3. Measure latency from far away Use a testing service that requests your site from several continents. Compare with the same measurement against the platform you left. Files: `docs/comparison.md` ### Done when - [ ] A second server serves the application from restored backups - [ ] The restore date and duration are recorded - [ ] Latency from at least two distant locations is measured and written down - [ ] The monthly cost of both options is compared in writing ### Watch out - Measure page load from somewhere far away, not from your desk. One server in one region will be markedly slower for distant visitors than an edge network, and this is the single largest thing you gave up. If most of your visitors are far from your server, that difference may outweigh the saving. ===== OPERATIONS.md ===== # Operations · Vercel ## Backup Nightly application data and nightly platform configuration, both dated, both off the server, several generations. Environment variables must exist in a secret store as well, because they are the slowest thing to reconstruct. ## Restore Fresh server, install the platform, restore its configuration, restore the data, verify the application, then repoint DNS. Do this on purpose and record the date and duration. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring External uptime against the production hostname, disk usage with room to react, and memory during builds. Build memory is the leading indicator of the outage you are most likely to have. ## Incident checklist For a bad deploy, roll back first and diagnose afterwards. For a build that kills production, add swap immediately and move builds off the box properly. For a certificate failure on previews, check the DNS token before anything else. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] A broken deploy is proved to leave the previous version serving - [ ] Preview isolation from production data is verified by an actual write - [ ] Both backup artefacts exist off the server and are dated - [ ] A restore has been completed on a second server and dated - [ ] Latency from distant locations has been measured and recorded ## Launch constraint Do not market omitted Vercel capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered. ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Hostname for the platform dashboard. Keep it off your production domain so an application misconfiguration cannot shadow it. COOLIFY_DOMAIN=deploy.example.com # Required. The wildcard used for per-branch preview hostnames. PREVIEW_WILDCARD_DOMAIN=*.preview.example.com # Required · secret. Your DNS provider's API token, scoped to the zone. Needed for wildcard certificate issuance. DNS_API_TOKEN=replace-with-provider-token # Required · secret. Set in the platform, never in the repository. PRODUCTION_DATABASE_URL=postgres://user:pass@db:5432/app # Required · secret. A different database from production. This variable is the isolation. PREVIEW_DATABASE_URL=postgres://user:pass@db:5432/app_preview # Required. Where both nightly artefacts go: application data and platform configuration. BACKUP_DEST=s3://your-bucket/deploy
# Vercel · indie build Coolify on one server, deploying your application on every push, giving each branch its own preview URL with its own certificate, keeping the previous release ready for an instant rollback, and backing up both the application data and the platform's own configuration off the machine. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Platform | Coolify, self-hosted | the only open option that does per-branch preview deployments through git webhooks, which is the feature people actually miss | | Host | One Linux server with 4 GB of memory, more if you build on it | Coolify idles around 2 GB and a Node build wants headroom, so 2 GB will fail during the build rather than at runtime | | Routing | Traefik, which Coolify manages, with Let's Encrypt | wildcard-capable routing is what makes per-branch preview hostnames possible | | Registry | Local image builds on the same server to start | a remote registry is the upgrade once builds start competing with production for memory | | Backups | The application's own data plus Coolify's configuration, both off the machine | losing the platform configuration means rebuilding every service by hand from memory | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **A Linux server with at least 4 GB of memory** · roughly 10 to 25 USD a month depending on provider and memory - Why: Coolify idles around 2 GB and a build wants the rest. On 2 GB the build kills production, which looks like a mystery outage. - Get it: Any virtual server provider. Choose a region close to your users, because that choice is now your entire content delivery strategy. - Verify: free -m reports 4 GB or more - [ ] **A second server for the restore drill** · a few USD if deleted the same day - Why: The drill proves nothing on the machine that already has the state. Create it for a day and destroy it after. - Get it: The same provider, smallest plan meeting the memory floor. - Verify: You can SSH to it - [ ] **A domain with API access to its DNS** · roughly 10 to 15 USD a year - Why: Wildcard certificates for preview hostnames need a DNS challenge, which needs an API token. A registrar without an API makes previews far harder. - Get it: Use a registrar or DNS host with an API, create a token scoped to the zone, and add both the A record and the wildcard record. - Verify: The wildcard hostname resolves and the token can create a TXT record - [ ] **A git host with webhook support** · free for personal repositories - Why: The whole pipeline is webhook driven. Without it you are deploying by hand, which is the thing you were trying to stop doing. - Get it: GitHub, GitLab or a self-hosted forge. Connect it in the platform and authorise the webhook. - Verify: A test push shows up as a deployment - [ ] **An off-server backup destination** · a few USD a month at these volumes - Why: Backups on the server die with the server. This includes the platform configuration, which exists nowhere else. - Get it: Object storage from any provider, with credentials scoped to write into one bucket. - Verify: A test file uploads and lists from your laptop ## Quick start ```sh ufw allow OpenSSH && ufw allow 80,443/tcp && ufw enable nmap -Pn YOUR_SERVER_IP ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - The global edge network, which is the largest thing you give up. - Automatic scaling under traffic you did not expect. - Framework-specific optimisations and edge functions. - Someone else carrying the pager. - edge/CDN performance - preview deployments - scaling - observability - image functions - team workflows - Next.js integration If one of those is essential to you, that is the reason to keep paying for Vercel, and the README should say so rather than pretend.
# Build brief · Vercel The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a git-driven deploy pipeline on your own server, with preview URLs per branch and instant rollback to replace Vercel. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Do not write a platform as a service. Coolify already does this job: git webhooks, per-branch preview deployments, automatic certificates and one-click databases, on a server you rent for the price of a sandwich. This is an operations task. Every phase is deploy, configure and verify, and the last one is a restore drill you actually perform. Be clear with yourself about what you are not getting back. The edge network is the product, and one server in one region is not a substitute for it. What you are buying with this work is control and a predictable bill, not equivalent performance. ### Stack (fixed, do not substitute) - Platform: Coolify, self-hosted · the only open option that does per-branch preview deployments through git webhooks, which is the feature people actually miss - Host: One Linux server with 4 GB of memory, more if you build on it · Coolify idles around 2 GB and a Node build wants headroom, so 2 GB will fail during the build rather than at runtime - Routing: Traefik, which Coolify manages, with Let's Encrypt · wildcard-capable routing is what makes per-branch preview hostnames possible - Registry: Local image builds on the same server to start · a remote registry is the upgrade once builds start competing with production for memory - Backups: The application's own data plus Coolify's configuration, both off the machine · losing the platform configuration means rebuilding every service by hand from memory ### Data model (create this before Phase 1) - This is infrastructure, so the state worth writing down is operational. - server · address, memory, disk, and what else is running on it - services · one per application, with its repository, branch, build command and environment variables - domains · production hostname, and the wildcard used for preview hostnames - secrets · every environment variable per service, kept somewhere that is not the server - backups · application data, Coolify configuration, and the date of the last restore drill ### Phase 1 · Server, DNS and the platform Build: Provision a server with at least 4 GB of memory. Install Coolify. Point an A record at it for the dashboard and a wildcard record for preview hostnames. Close the firewall to everything except SSH and the web ports. Trap: Preview deployments need a wildcard DNS record, and a wildcard needs a wildcard certificate, which needs a DNS challenge rather than the usual HTTP one. That means an API token for your DNS provider. Sort this out in Phase 1, because discovering it when your first preview URL fails to get a certificate is a bad afternoon. Done when: The Coolify dashboard is reachable over HTTPS with a valid certificate and requires a login. A wildcard DNS record resolves to the server. A port scan from another machine shows only the ports you intended. Do not build yet: No applications deployed yet. ### Phase 2 · First deploy from a push Build: Connect the git repository, configure the build, and add the webhook so a push triggers a deploy. Set environment variables in the platform rather than in the repository. Watch the first build log all the way through. Trap: Builds run on the same server as production by default, and a Node build can consume more memory than the running application. On a 4 GB server this shows up as the build killing production, which looks like a random outage. Watch memory during the first build, and add swap or move builds elsewhere before it bites. Done when: A push to the main branch produces a new deployment without manual intervention. The application answers on the production hostname over HTTPS. Memory during the build was observed and stayed within the server's capacity, or swap was added after seeing that it did not. Do not build yet: No previews and no rollback yet. ### Phase 3 · Preview deployments per branch Build: Enable preview deployments so each branch or pull request builds to its own hostname under the wildcard, with its own certificate. Give previews their own environment variables so they never write to production data. Trap: A preview environment that inherits production environment variables will happily write to the production database, and the first time it does you will find out through data rather than through an error. Give previews their own database and their own variables from the very first preview, not after the first accident. Done when: Opening a pull request produces a working URL with a valid certificate within a few minutes. That preview is verified to be writing to a non-production database. Closing the pull request removes the preview and frees its resources. Do not build yet: No rollback or backups yet. ### Phase 4 · Rollback and health Build: Keep previous images so a rollback is immediate. Add a health check so a failed deployment does not replace a working one. Confirm both by deliberately deploying something broken. Trap: A deployment strategy that stops the old container before the new one is healthy gives you downtime on every deploy and an outage on every bad one. Configure the health check and confirm the old container keeps serving until the new one passes. Done when: Deploying a deliberately broken commit leaves the previous version serving, verified by requesting the site during the failed deploy. Rolling back to the previous deployment restores service in under a minute. The health check endpoint touches the database rather than only returning a static response. Do not build yet: Backups come next. ### Phase 5 · Backups, including the platform itself Build: Back up the application's data and Coolify's own configuration, both off the server, both dated. The platform configuration is the part everyone forgets and the part that takes longest to rebuild by hand. Trap: Backing up your database and not the platform configuration means a restore leaves you re-entering every service, domain and environment variable from memory. The environment variables in particular exist nowhere else once the server is gone. Done when: Dated backups of both the application data and the platform configuration land off the server nightly. The environment variables for every service exist somewhere that is not the server. A missing backup produces an alert. Do not build yet: The restore drill is the last phase and it is the one that counts. ### Phase 6 · The restore drill and the honest comparison Build: Provision a second server, install the platform, restore the configuration and the data, and bring the application up. Record the date and the duration. Then compare total cost and performance honestly against the platform you left. Trap: Measure page load from somewhere far away, not from your desk. One server in one region will be markedly slower for distant visitors than an edge network, and this is the single largest thing you gave up. If most of your visitors are far from your server, that difference may outweigh the saving. Done when: A second server serves the application from restored backups, with the same data, and the date and duration are recorded. A latency comparison from at least two distant locations is written down alongside the monthly cost of both options. Do not build yet: Nothing after this. ### Out of scope (and why) - The edge network. This is the product, and one server in one region does not approximate it. Distant visitors will notice. - Automatic scaling under load. Your server has the capacity it has, and a traffic spike is now your problem at three in the morning. - Image optimisation, edge functions and the framework-specific features that only exist because the platform builds them. - Someone else being responsible for uptime. That is now you, and it is the part that costs more than the bill you saved. ### README must contain - The server specification, and that builds and production share its memory - That preview deployments need a wildcard record and a DNS challenge, with the provider token this requires - That preview environments use their own database, and how that isolation was verified - That backups cover both application data and the platform configuration, and where the environment variables live - The date and duration of the last restore drill, and the latency comparison against the platform you left
# Agent instructions · Vercel indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Coolify, self-hosted, One Linux server with 4 GB of memory, more if you build on it, Traefik, which Coolify manages, with Let's Encrypt, Local image builds on the same server to start, The application's own data plus Coolify's configuration, both off the machine. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Preview deployments need a wildcard DNS record, and a wildcard needs a wildcard certificate, which needs a DNS challenge rather than the usual HTTP one. That means an API token for your DNS provider. Sort this out in Phase 1, because discovering it when your first preview URL fails to get a certificate is a bad afternoon. - Builds run on the same server as production by default, and a Node build can consume more memory than the running application. On a 4 GB server this shows up as the build killing production, which looks like a random outage. Watch memory during the first build, and add swap or move builds elsewhere before it bites. - A preview environment that inherits production environment variables will happily write to the production database, and the first time it does you will find out through data rather than through an error. Give previews their own database and their own variables from the very first preview, not after the first accident. - A deployment strategy that stops the old container before the new one is healthy gives you downtime on every deploy and an outage on every bad one. Configure the health check and confirm the old container keeps serving until the new one passes. - Backing up your database and not the platform configuration means a restore leaves you re-entering every service, domain and environment variable from memory. The environment variables in particular exist nowhere else once the server is gone.
# Build plan · Vercel Coolify on one server, deploying your application on every push, giving each branch its own preview URL with its own certificate, keeping the previous release ready for an instant rollback, and backing up both the application data and the platform's own configuration off the machine. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Server, DNS and the platform Coolify runs, you can reach its dashboard safely, and DNS is ready for preview hostnames. ### Steps 1. Provision and close the server A non-root user, SSH keys only, and a firewall allowing SSH, 80 and 443. Verify with a scan from your laptop rather than by reading the configuration. ```sh ufw allow OpenSSH && ufw allow 80,443/tcp && ufw enable nmap -Pn YOUR_SERVER_IP ``` 2. Install Coolify Its installation script sets up Docker and the platform. Change the initial dashboard credentials immediately. ```sh curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash ``` 3. Set up DNS including the wildcard An A record for the dashboard and for production, plus a wildcard for preview hostnames. Add a DNS provider API token so wildcard certificates can be issued. ```sh dig +short app.example.com dig +short test.preview.example.com ``` ### Done when - [ ] The dashboard loads over HTTPS with a valid certificate and requires login - [ ] A wildcard hostname resolves to the server - [ ] Only the intended ports are open, verified from another machine - [ ] The default dashboard credentials have been changed ### Watch out - Preview deployments need a wildcard DNS record, and a wildcard needs a wildcard certificate, which needs a DNS challenge rather than the usual HTTP one. That means an API token for your DNS provider. Sort this out in Phase 1, because discovering it when your first preview URL fails to get a certificate is a bad afternoon. ## Phase 2 · First deploy from a push Pushing to the main branch deploys, without you touching the server. ### Steps 1. Connect the repository and configure the build Point at the repository and branch. Let the platform detect the build, then check what it detected rather than assuming it guessed right. 2. Move environment variables into the platform Every secret goes in the platform's own storage, never into the repository. This is also what makes preview environments possible later. 3. Watch memory during the first build Run top on the server while the build runs. This one observation prevents the most common self-hosting outage. ```sh free -m top -o %MEM ``` ### Done when - [ ] A push to main triggers a deploy with no manual step - [ ] The application answers on the production hostname over HTTPS - [ ] No secret is present in the repository - [ ] Build memory was observed and the server coped, or swap was added ### Watch out - Builds run on the same server as production by default, and a Node build can consume more memory than the running application. On a 4 GB server this shows up as the build killing production, which looks like a random outage. Watch memory during the first build, and add swap or move builds elsewhere before it bites. ## Phase 3 · Preview deployments per branch Every pull request gets its own URL, which is the feature you were actually paying for. ### Steps 1. Turn on preview deployments Configure the wildcard domain for previews and confirm certificate issuance works through the DNS challenge. 2. Give previews their own data A separate database, seeded rather than copied from production. Override the connection variable at the preview level. 3. Prove the isolation Write a record from a preview and confirm it is absent from production. Do this once, deliberately, before you trust it. ### Done when - [ ] A pull request produces a working preview URL with a valid certificate - [ ] A write from a preview does not appear in the production database - [ ] Closing the pull request removes the preview - [ ] Preview environment variables differ from production ### Watch out - A preview environment that inherits production environment variables will happily write to the production database, and the first time it does you will find out through data rather than through an error. Give previews their own database and their own variables from the very first preview, not after the first accident. ## Phase 4 · Rollback and health A bad deploy is a one-click problem rather than an emergency. ### Steps 1. Add a real health check An endpoint that queries the database. A static endpoint returns healthy while the application is unable to serve anyone, which is worse than no check. Files: `src/health` 2. Configure the deployment to wait for health The old container keeps serving until the new one passes. Verify by watching requests during a deploy. 3. Break it on purpose and roll back Push a commit that fails to start. Confirm the site stays up, then roll back and time it. ### Done when - [ ] A broken deploy leaves the previous version serving - [ ] Rollback restores service in under a minute - [ ] The health check fails when the database is unreachable - [ ] Requests during a successful deploy are not dropped ### Watch out - A deployment strategy that stops the old container before the new one is healthy gives you downtime on every deploy and an outage on every bad one. Configure the health check and confirm the old container keeps serving until the new one passes. ## Phase 5 · Backups, including the platform itself You can rebuild everything, including the configuration that describes everything. ### Steps 1. Back up application data Database dumps and any uploaded files, dated, off the machine, several generations retained. Files: `scripts/backup.sh` 2. Back up the platform configuration Coolify's own database and configuration directory. This is what stores your services, domains and variables. Files: `scripts/backup.sh` 3. Alert on a missing backup Check each morning that today's files exist. Silence is not evidence that a backup ran. Files: `scripts/check_backup.sh` ### Done when - [ ] Both backup artefacts land off the server nightly - [ ] Every service's environment variables exist outside the server - [ ] A suppressed backup job produces an alert - [ ] Several generations are retained rather than one overwritten file ### Watch out - Backing up your database and not the platform configuration means a restore leaves you re-entering every service, domain and environment variable from memory. The environment variables in particular exist nowhere else once the server is gone. ## Not in this build - The global edge network, which is the largest thing you give up. - Automatic scaling under traffic you did not expect. - Framework-specific optimisations and edge functions. - Someone else carrying the pager. ## After v1, if you want it - Moving builds to a separate machine once they compete with production - A staging environment restored nightly from production backups, which also exercises the restore - A content delivery network in front for static assets, which recovers some of the edge benefit cheaply
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Hostname for the platform dashboard. Keep it off your production domain so an application misconfiguration cannot shadow it. COOLIFY_DOMAIN=deploy.example.com # Required. The wildcard used for per-branch preview hostnames. PREVIEW_WILDCARD_DOMAIN=*.preview.example.com # Required · secret. Your DNS provider's API token, scoped to the zone. Needed for wildcard certificate issuance. DNS_API_TOKEN=replace-with-provider-token # Required · secret. Set in the platform, never in the repository. PRODUCTION_DATABASE_URL=postgres://user:pass@db:5432/app # Required · secret. A different database from production. This variable is the isolation. PREVIEW_DATABASE_URL=postgres://user:pass@db:5432/app_preview # Required. Where both nightly artefacts go: application data and platform configuration. BACKUP_DEST=s3://your-bucket/deploy
# Vercel · product brief ## Problem You can deploy to a VPS or self-host PaaS, but Vercel's edge network, preview deployments, CI/CD, observability, and Next.js integration are the paid value. ## Product outcome A deploy pipeline that triggers on push, gives every branch an isolated preview URL, rolls back in under a minute, backs up both the data and the configuration that describes it, and whose real cost and latency you have measured rather than assumed. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - VPS/cloud account - domain/DNS - CI/CD - reverse proxy - SSL - monitoring - backups ## Explicit non-goals for v1 - The global edge network, which is the largest thing you give up. - Automatic scaling under traffic you did not expect. - Framework-specific optimisations and edge functions. - Someone else carrying the pager. - edge/CDN performance - preview deployments - scaling - observability - image functions - team workflows - Next.js integration ## Success criteria - A broken deploy is proved to leave the previous version serving - Preview isolation from production data is verified by an actual write - Both backup artefacts exist off the server and are dated - A restore has been completed on a second server and dated - Latency from distant locations has been measured and recorded
# Build brief · Vercel The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a git-driven deploy pipeline on your own server, with preview URLs per branch and instant rollback to replace Vercel. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Do not write a platform as a service. Coolify already does this job: git webhooks, per-branch preview deployments, automatic certificates and one-click databases, on a server you rent for the price of a sandwich. This is an operations task. Every phase is deploy, configure and verify, and the last one is a restore drill you actually perform. Be clear with yourself about what you are not getting back. The edge network is the product, and one server in one region is not a substitute for it. What you are buying with this work is control and a predictable bill, not equivalent performance. ### Stack (fixed, do not substitute) - Platform: Coolify, self-hosted · the only open option that does per-branch preview deployments through git webhooks, which is the feature people actually miss - Host: One Linux server with 4 GB of memory, more if you build on it · Coolify idles around 2 GB and a Node build wants headroom, so 2 GB will fail during the build rather than at runtime - Routing: Traefik, which Coolify manages, with Let's Encrypt · wildcard-capable routing is what makes per-branch preview hostnames possible - Registry: Local image builds on the same server to start · a remote registry is the upgrade once builds start competing with production for memory - Backups: The application's own data plus Coolify's configuration, both off the machine · losing the platform configuration means rebuilding every service by hand from memory ### Data model (create this before Phase 1) - This is infrastructure, so the state worth writing down is operational. - server · address, memory, disk, and what else is running on it - services · one per application, with its repository, branch, build command and environment variables - domains · production hostname, and the wildcard used for preview hostnames - secrets · every environment variable per service, kept somewhere that is not the server - backups · application data, Coolify configuration, and the date of the last restore drill ### Phase 1 · Server, DNS and the platform Build: Provision a server with at least 4 GB of memory. Install Coolify. Point an A record at it for the dashboard and a wildcard record for preview hostnames. Close the firewall to everything except SSH and the web ports. Trap: Preview deployments need a wildcard DNS record, and a wildcard needs a wildcard certificate, which needs a DNS challenge rather than the usual HTTP one. That means an API token for your DNS provider. Sort this out in Phase 1, because discovering it when your first preview URL fails to get a certificate is a bad afternoon. Done when: The Coolify dashboard is reachable over HTTPS with a valid certificate and requires a login. A wildcard DNS record resolves to the server. A port scan from another machine shows only the ports you intended. Do not build yet: No applications deployed yet. ### Phase 2 · First deploy from a push Build: Connect the git repository, configure the build, and add the webhook so a push triggers a deploy. Set environment variables in the platform rather than in the repository. Watch the first build log all the way through. Trap: Builds run on the same server as production by default, and a Node build can consume more memory than the running application. On a 4 GB server this shows up as the build killing production, which looks like a random outage. Watch memory during the first build, and add swap or move builds elsewhere before it bites. Done when: A push to the main branch produces a new deployment without manual intervention. The application answers on the production hostname over HTTPS. Memory during the build was observed and stayed within the server's capacity, or swap was added after seeing that it did not. Do not build yet: No previews and no rollback yet. ### Phase 3 · Preview deployments per branch Build: Enable preview deployments so each branch or pull request builds to its own hostname under the wildcard, with its own certificate. Give previews their own environment variables so they never write to production data. Trap: A preview environment that inherits production environment variables will happily write to the production database, and the first time it does you will find out through data rather than through an error. Give previews their own database and their own variables from the very first preview, not after the first accident. Done when: Opening a pull request produces a working URL with a valid certificate within a few minutes. That preview is verified to be writing to a non-production database. Closing the pull request removes the preview and frees its resources. Do not build yet: No rollback or backups yet. ### Phase 4 · Rollback and health Build: Keep previous images so a rollback is immediate. Add a health check so a failed deployment does not replace a working one. Confirm both by deliberately deploying something broken. Trap: A deployment strategy that stops the old container before the new one is healthy gives you downtime on every deploy and an outage on every bad one. Configure the health check and confirm the old container keeps serving until the new one passes. Done when: Deploying a deliberately broken commit leaves the previous version serving, verified by requesting the site during the failed deploy. Rolling back to the previous deployment restores service in under a minute. The health check endpoint touches the database rather than only returning a static response. Do not build yet: Backups come next. ### Phase 5 · Backups, including the platform itself Build: Back up the application's data and Coolify's own configuration, both off the server, both dated. The platform configuration is the part everyone forgets and the part that takes longest to rebuild by hand. Trap: Backing up your database and not the platform configuration means a restore leaves you re-entering every service, domain and environment variable from memory. The environment variables in particular exist nowhere else once the server is gone. Done when: Dated backups of both the application data and the platform configuration land off the server nightly. The environment variables for every service exist somewhere that is not the server. A missing backup produces an alert. Do not build yet: The restore drill is the last phase and it is the one that counts. ### Phase 6 · The restore drill and the honest comparison Build: Provision a second server, install the platform, restore the configuration and the data, and bring the application up. Record the date and the duration. Then compare total cost and performance honestly against the platform you left. Trap: Measure page load from somewhere far away, not from your desk. One server in one region will be markedly slower for distant visitors than an edge network, and this is the single largest thing you gave up. If most of your visitors are far from your server, that difference may outweigh the saving. Done when: A second server serves the application from restored backups, with the same data, and the date and duration are recorded. A latency comparison from at least two distant locations is written down alongside the monthly cost of both options. Do not build yet: Nothing after this. ### Out of scope (and why) - The edge network. This is the product, and one server in one region does not approximate it. Distant visitors will notice. - Automatic scaling under load. Your server has the capacity it has, and a traffic spike is now your problem at three in the morning. - Image optimisation, edge functions and the framework-specific features that only exist because the platform builds them. - Someone else being responsible for uptime. That is now you, and it is the part that costs more than the bill you saved. ### README must contain - The server specification, and that builds and production share its memory - That preview deployments need a wildcard record and a DNS challenge, with the provider token this requires - That preview environments use their own database, and how that isolation was verified - That backups cover both application data and the platform configuration, and where the environment variables live - The date and duration of the last restore drill, and the latency comparison against the platform you left
# Architecture · Vercel ## Stack | Part | Choice | Why | | --- | --- | --- | | Platform | Coolify, self-hosted | the only open option that does per-branch preview deployments through git webhooks, which is the feature people actually miss | | Host | One Linux server with 4 GB of memory, more if you build on it | Coolify idles around 2 GB and a Node build wants headroom, so 2 GB will fail during the build rather than at runtime | | Routing | Traefik, which Coolify manages, with Let's Encrypt | wildcard-capable routing is what makes per-branch preview hostnames possible | | Registry | Local image builds on the same server to start | a remote registry is the upgrade once builds start competing with production for memory | | Backups | The application's own data plus Coolify's configuration, both off the machine | losing the platform configuration means rebuilding every service by hand from memory | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Platform | Builds, deployments, routing and certificates. | Dokploy is lighter and lacks preview environments, which is the feature this whole build exists for. Swapping means giving that up. | | Routing | Hostnames, TLS and preview wildcards. | Managed by the platform. Replacing it means leaving the platform, not reconfiguring it. | | Build | Turning a commit into a runnable image. | Move builds to a separate machine or a hosted runner as soon as they compete with production for memory. This is the first thing to outgrow one server. | | Data | Application databases and uploads, per environment. | Preview and production isolation must survive any change here. It is the invariant, not the implementation. | | Backups | Data and platform configuration, off the machine. | Destination is replaceable. Including the platform configuration is not optional. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `COOLIFY_DOMAIN` · required · Hostname for the platform dashboard. Keep it off your production domain so an application misconfiguration cannot shadow it. - `PREVIEW_WILDCARD_DOMAIN` · required · The wildcard used for per-branch preview hostnames. - `DNS_API_TOKEN` · required, secret · Your DNS provider's API token, scoped to the zone. Needed for wildcard certificate issuance. - `PRODUCTION_DATABASE_URL` · required, secret · Set in the platform, never in the repository. - `PREVIEW_DATABASE_URL` · required, secret · A different database from production. This variable is the isolation. - `BACKUP_DEST` · required · Where both nightly artefacts go: application data and platform configuration. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it.
# Agent instructions · Vercel product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Coolify, self-hosted, One Linux server with 4 GB of memory, more if you build on it, Traefik, which Coolify manages, with Let's Encrypt, Local image builds on the same server to start, The application's own data plus Coolify's configuration, both off the machine. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Preview deployments need a wildcard DNS record, and a wildcard needs a wildcard certificate, which needs a DNS challenge rather than the usual HTTP one. That means an API token for your DNS provider. Sort this out in Phase 1, because discovering it when your first preview URL fails to get a certificate is a bad afternoon. - Builds run on the same server as production by default, and a Node build can consume more memory than the running application. On a 4 GB server this shows up as the build killing production, which looks like a random outage. Watch memory during the first build, and add swap or move builds elsewhere before it bites. - A preview environment that inherits production environment variables will happily write to the production database, and the first time it does you will find out through data rather than through an error. Give previews their own database and their own variables from the very first preview, not after the first accident. - A deployment strategy that stops the old container before the new one is healthy gives you downtime on every deploy and an outage on every bad one. Configure the health check and confirm the old container keeps serving until the new one passes. - Backing up your database and not the platform configuration means a restore leaves you re-entering every service, domain and environment variable from memory. The environment variables in particular exist nowhere else once the server is gone. - Measure page load from somewhere far away, not from your desk. One server in one region will be markedly slower for distant visitors than an edge network, and this is the single largest thing you gave up. If most of your visitors are far from your server, that difference may outweigh the saving.
# Delivery milestones · Vercel Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Server, DNS and the platform Coolify runs, you can reach its dashboard safely, and DNS is ready for preview hostnames. ### Steps 1. Provision and close the server A non-root user, SSH keys only, and a firewall allowing SSH, 80 and 443. Verify with a scan from your laptop rather than by reading the configuration. ```sh ufw allow OpenSSH && ufw allow 80,443/tcp && ufw enable nmap -Pn YOUR_SERVER_IP ``` 2. Install Coolify Its installation script sets up Docker and the platform. Change the initial dashboard credentials immediately. ```sh curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash ``` 3. Set up DNS including the wildcard An A record for the dashboard and for production, plus a wildcard for preview hostnames. Add a DNS provider API token so wildcard certificates can be issued. ```sh dig +short app.example.com dig +short test.preview.example.com ``` ### Done when - [ ] The dashboard loads over HTTPS with a valid certificate and requires login - [ ] A wildcard hostname resolves to the server - [ ] Only the intended ports are open, verified from another machine - [ ] The default dashboard credentials have been changed ### Watch out - Preview deployments need a wildcard DNS record, and a wildcard needs a wildcard certificate, which needs a DNS challenge rather than the usual HTTP one. That means an API token for your DNS provider. Sort this out in Phase 1, because discovering it when your first preview URL fails to get a certificate is a bad afternoon. ## M2 · First deploy from a push Pushing to the main branch deploys, without you touching the server. ### Steps 1. Connect the repository and configure the build Point at the repository and branch. Let the platform detect the build, then check what it detected rather than assuming it guessed right. 2. Move environment variables into the platform Every secret goes in the platform's own storage, never into the repository. This is also what makes preview environments possible later. 3. Watch memory during the first build Run top on the server while the build runs. This one observation prevents the most common self-hosting outage. ```sh free -m top -o %MEM ``` ### Done when - [ ] A push to main triggers a deploy with no manual step - [ ] The application answers on the production hostname over HTTPS - [ ] No secret is present in the repository - [ ] Build memory was observed and the server coped, or swap was added ### Watch out - Builds run on the same server as production by default, and a Node build can consume more memory than the running application. On a 4 GB server this shows up as the build killing production, which looks like a random outage. Watch memory during the first build, and add swap or move builds elsewhere before it bites. ## M3 · Preview deployments per branch Every pull request gets its own URL, which is the feature you were actually paying for. ### Steps 1. Turn on preview deployments Configure the wildcard domain for previews and confirm certificate issuance works through the DNS challenge. 2. Give previews their own data A separate database, seeded rather than copied from production. Override the connection variable at the preview level. 3. Prove the isolation Write a record from a preview and confirm it is absent from production. Do this once, deliberately, before you trust it. ### Done when - [ ] A pull request produces a working preview URL with a valid certificate - [ ] A write from a preview does not appear in the production database - [ ] Closing the pull request removes the preview - [ ] Preview environment variables differ from production ### Watch out - A preview environment that inherits production environment variables will happily write to the production database, and the first time it does you will find out through data rather than through an error. Give previews their own database and their own variables from the very first preview, not after the first accident. ## M4 · Rollback and health A bad deploy is a one-click problem rather than an emergency. ### Steps 1. Add a real health check An endpoint that queries the database. A static endpoint returns healthy while the application is unable to serve anyone, which is worse than no check. Files: `src/health` 2. Configure the deployment to wait for health The old container keeps serving until the new one passes. Verify by watching requests during a deploy. 3. Break it on purpose and roll back Push a commit that fails to start. Confirm the site stays up, then roll back and time it. ### Done when - [ ] A broken deploy leaves the previous version serving - [ ] Rollback restores service in under a minute - [ ] The health check fails when the database is unreachable - [ ] Requests during a successful deploy are not dropped ### Watch out - A deployment strategy that stops the old container before the new one is healthy gives you downtime on every deploy and an outage on every bad one. Configure the health check and confirm the old container keeps serving until the new one passes. ## M5 · Backups, including the platform itself You can rebuild everything, including the configuration that describes everything. ### Steps 1. Back up application data Database dumps and any uploaded files, dated, off the machine, several generations retained. Files: `scripts/backup.sh` 2. Back up the platform configuration Coolify's own database and configuration directory. This is what stores your services, domains and variables. Files: `scripts/backup.sh` 3. Alert on a missing backup Check each morning that today's files exist. Silence is not evidence that a backup ran. Files: `scripts/check_backup.sh` ### Done when - [ ] Both backup artefacts land off the server nightly - [ ] Every service's environment variables exist outside the server - [ ] A suppressed backup job produces an alert - [ ] Several generations are retained rather than one overwritten file ### Watch out - Backing up your database and not the platform configuration means a restore leaves you re-entering every service, domain and environment variable from memory. The environment variables in particular exist nowhere else once the server is gone. ## M6 · The restore drill and the honest comparison (production only) You have rebuilt it on a new server, and you know what this actually costs. ### Steps 1. Restore onto a clean server New server, fresh platform install, restore the configuration, restore the data, point DNS at it temporarily and verify. 2. Record the date and duration In the README. A restore you did once and dated is worth more than a runbook you have never followed. Files: `README.md` 3. Measure latency from far away Use a testing service that requests your site from several continents. Compare with the same measurement against the platform you left. Files: `docs/comparison.md` ### Done when - [ ] A second server serves the application from restored backups - [ ] The restore date and duration are recorded - [ ] Latency from at least two distant locations is measured and written down - [ ] The monthly cost of both options is compared in writing ### Watch out - Measure page load from somewhere far away, not from your desk. One server in one region will be markedly slower for distant visitors than an edge network, and this is the single largest thing you gave up. If most of your visitors are far from your server, that difference may outweigh the saving.
# Operations · Vercel ## Backup Nightly application data and nightly platform configuration, both dated, both off the server, several generations. Environment variables must exist in a secret store as well, because they are the slowest thing to reconstruct. ## Restore Fresh server, install the platform, restore its configuration, restore the data, verify the application, then repoint DNS. Do this on purpose and record the date and duration. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring External uptime against the production hostname, disk usage with room to react, and memory during builds. Build memory is the leading indicator of the outage you are most likely to have. ## Incident checklist For a bad deploy, roll back first and diagnose afterwards. For a build that kills production, add swap immediately and move builds off the box properly. For a certificate failure on previews, check the DNS token before anything else. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] A broken deploy is proved to leave the previous version serving - [ ] Preview isolation from production data is verified by an actual write - [ ] Both backup artefacts exist off the server and are dated - [ ] A restore has been completed on a second server and dated - [ ] Latency from distant locations has been measured and recorded ## Launch constraint Do not market omitted Vercel capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered.
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Hostname for the platform dashboard. Keep it off your production domain so an application misconfiguration cannot shadow it. COOLIFY_DOMAIN=deploy.example.com # Required. The wildcard used for per-branch preview hostnames. PREVIEW_WILDCARD_DOMAIN=*.preview.example.com # Required · secret. Your DNS provider's API token, scoped to the zone. Needed for wildcard certificate issuance. DNS_API_TOKEN=replace-with-provider-token # Required · secret. Set in the platform, never in the repository. PRODUCTION_DATABASE_URL=postgres://user:pass@db:5432/app # Required · secret. A different database from production. This variable is the isolation. PREVIEW_DATABASE_URL=postgres://user:pass@db:5432/app_preview # Required. Where both nightly artefacts go: application data and platform configuration. BACKUP_DEST=s3://your-bucket/deploy
$ choose a build depth, inspect the files, then open the complete pack in your agent
They pay because deploys and previews are boring infrastructure that must not break.
xedge/CDN performance
xpreview deployments
xscaling
xobservability
ximage functions
xteam workflows
xNext.js integration
Don't feel like building it? These folks already made it free.
all 4 free alternatives to Vercel →· no votes, no pay-to-list · just what's real
Vercel pricing
| plan | monthly | annual (per mo) | what you get |
|---|---|---|---|
| hobby | $0 | $0 | 1 developer; non-commercial/personal use; 1,000,000 edge requests/month, 100 GB data transfer, 1,000,000 function invocations and 4 active CPU hours/month. |
| pro | $20/user | — | 1 developer seat; includes $20/month usage credit, 10,000,000 edge requests and 1 TB transfer before usage charges. |
| enterprise | custom | — | Custom seats, usage allowances and support; numeric limits and price are not public. |
free tierPersonal/non-commercial use: 1 developer, 1M edge requests/month, 100 GB transfer, 1M function invocations and 4 active CPU hours/month.
billingHobby free; Pro monthly only with no annual self-serve plan; Enterprise custom
hidden costsPro overages include $2 per additional 1M edge requests and $0.15/GB transfer after included usage; new teams have a default $200 on-demand budget unless a hard limit is set. Add-ons include SAML SSO $300/month, HIPAA BAA $350/month, Advanced Deployment Protection $150/month, Static IP $100/project/month, Preview URL Suffix $100/month, extra microfrontend projects $250/month, Web Analytics Plus $10/month and Speed Insights $10/project/month.
verified 2026-08-11 · source ↗
Is Vercel free?
The free Hobby plan covers non-commercial projects; 1M requests and 100 GB monthly. Paid is Pro at $20/mo (checked 2026-08-07).
Vibecode Vercel
Kinda. The core of Vercel is buildable in a weekend with the prompt on this page, but there are real gaps: edge/CDN performance, preview deployments. Read the honest list above before committing.
How much does Vercel cost?
Vercel costs about $20/month (Pro, checked 2026-08-07), which is $240 per year.
What do I lose by replacing Vercel?
Honestly: edge/CDN performance; preview deployments; scaling; observability; image functions; team workflows; Next.js integration. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to Vercel?
Yes: Coolify (Deploy Next.js or any web container from Git on your own server; previews and framework magic are less automatic.) Dokploy (A newer self-hosted PaaS for apps and databases, with previews, TLS, logs, and backups in the same control plane.) Cloudflare Pages Free (Git-to-edge hosting with previews and Workers functions; a strong fit unless your app depends on Vercel's Next.js-only tricks.) All 4 curated free alternatives are at vibecodeit.com/vercel/alternatives. The prompt is for when you want it exactly your way.