[{"data":1,"prerenderedAt":184},["ShallowReactive",2],{"seo-verification":3,"blog-host-calcom-on-your-own-vps-ma-en":6,"promo-bar-likely":183},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"key":7,"data":8},"blog-host-calcom-on-your-own-vps-ma-en",{"id":9,"slug":10,"slugs":11,"title":15,"excerpt":16,"readTime":17,"views":18,"isPinned":19,"noindex":19,"publishedAt":20,"updatedAt":21,"category":22,"categories":28,"featuredImage":30,"bgImage":31,"posterImage":32,"relatedSolution":33,"intro":35,"sections":36,"ctaTitle":118,"ctaBody":119,"ctaButton":120,"ctaUrl":121,"relatedPosts":122},88,"host-calcom-on-your-own-vps",{"fr":12,"en":10,"ar":13,"es":14},"heberger-cal-com","استضافة-calcom-على-خادمك-الافتراضي-الخاص-vps","alojar-cal-com-en-un-vps","Host Cal.com on Your VPS: Complete Docker Guide","Deploy Cal.com on a VPS with Docker, PostgreSQL and reverse proxy. Fix CLIENT_FETCH_ERROR, first-boot invariants, safe updates and troubleshooting.",9,2,false,"2026-03-24T00:00:00+00:00","2026-09-07T11:26:10+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},7,"Self-hosting","self-hosting","bg-indigo-500\u002F10 text-indigo-400","cloud",[29],{"id":23,"name":24,"slug":25,"color":26,"icon":27},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Fheberger-cal-com-poster.svg",{"categorySlug":25,"appSlug":34},"cal-com","Calendly is convenient but proprietary, limited in its free plan and hungry for your meeting data. Cal.com is its open source equivalent: appointment scheduling, calendar and video integrations, all hostable on your own VPS. This guide covers the initial deployment, reverse proxy setup, the CLIENT_FETCH_ERROR that has been blocking new Docker installations since early 2026, and the most common errors.",[37,42,52,56,82,86,90,106,110,114],{"type":38,"title":39,"body":40,"items":41},"h2","Why self-host Cal.com on a VPS","Cal.com is a Next.js application backed by PostgreSQL that handles appointment scheduling, event types, availability and synchronization with calendars (Google, CalDAV, Office 365). Self-hosting answers a specific need: controlling the availability and appointment-booking data of your clients, which normally passes through a third-party US service. On a VPS, you eliminate the free plan's limits (a single event type, imposed branding), you connect your own Google\u002Fvideo API keys and you embed the booking widget directly into your site under your domain. Since Cal.com is a persistent Node application with a database and a production build, it requires a VPS — shared hosting can neither run the process nor host PostgreSQL.",[],{"type":43,"title":44,"items":45},"ul","Concrete benefits of a self-hosted Cal.com",[46,47,48,49,50,51],"Multiple event types: 15-min interviews, 30-min demos, group workshops, without a paywall.","Booking data on your side: no leak of clients' contact details and time slots to a third-party SaaS.","Full white-label: the booking link carries your domain, not a vendor's.","Webhooks and API: trigger automations (CRM, billing) on every appointment booked.","Google Calendar, CalDAV and video integrations (Jitsi, Google Meet) configured with your own keys.","Team bookings and round-robin to distribute appointments among several collaborators.",{"type":38,"title":53,"body":54,"items":55},"Technical requirements","Cal.com is more demanding than the self-hosting average because of its Next.js foundation and the build step. Plan for 2 vCPUs, 4 GB of RAM and 20 GB of disk for a comfortable team instance; 2 GB of RAM can suffice for individual use but the initial build is tighter. You need Docker and Docker Compose, a PostgreSQL database (included in the official compose), a domain `rdv.yourdomain.com` pointed at the VPS, and several mandatory environment variables: `NEXTAUTH_SECRET`, `CALENDSO_ENCRYPTION_KEY` (randomly generated keys) and `NEXT_PUBLIC_WEBAPP_URL` set to your final HTTPS URL. For calendar sync and video, prepare the Google OAuth credentials.",[],{"type":57,"title":58,"steps":59,"items":81},"steps","Deploy Cal.com step by step",[60,63,66,69,72,75,78],{"title":61,"body":62},"Clone the Docker deployment repository","On the VPS: `git clone https:\u002F\u002Fgithub.com\u002Fcalcom\u002Fdocker.git cal-docker && cd cal-docker`. This repository provides a `docker-compose.yml` and an `.env.example` file to adapt.",{"title":64,"body":65},"Generate the secrets and configure the environment","Copy `.env.example` to `.env`, then generate the keys: `openssl rand -base64 32` for `NEXTAUTH_SECRET` and for `CALENDSO_ENCRYPTION_KEY`. Set `NEXT_PUBLIC_WEBAPP_URL=https:\u002F\u002Frdv.yourdomain.com` and the PostgreSQL credentials. **Save these three values in a secrets manager immediately** — you will not be able to change them after the first boot without losing all your integrations.",{"title":67,"body":68},"Add NEXTAUTH_URL_INTERNAL to prevent CLIENT_FETCH_ERROR","Add `NEXTAUTH_URL_INTERNAL=http:\u002F\u002Fcalcom:3000` to your `.env`. Without this variable, the Next.js container tries to resolve its own public domain (`rdv.yourdomain.com`) from inside the Docker network, where the external DNS does not respond — every server-side authentication request fails with `CLIENT_FETCH_ERROR`. `NEXTAUTH_URL_INTERNAL` short-circuits that resolution by pointing directly to the Docker service name (`calcom` is the service name in `docker-compose.yml`). This variable is separate from `NEXTAUTH_URL`: both must be present.",{"title":70,"body":71},"Build and launch the stack","Start with `docker compose up -d`. The first startup builds the Next.js image and applies the Prisma migrations on PostgreSQL — this is the longest step, follow it with `docker compose logs -f`.",{"title":73,"body":74},"Logging in for the first time","Open the URL: Cal.com redirects you to its first-run setup wizard (`\u002Fauth\u002Fsetup`), where you create YOUR administrator account. Do this as soon as the installation finishes: nothing protects this wizard for as long as the first account does not exist.",{"title":76,"body":77},"Create the account and configure availability","Set your time slots and a first event type. Test an end-to-end booking to validate the full chain before connecting integrations.",{"title":79,"body":80},"Connect calendar and video","In the integrations, add your Google OAuth credentials for bidirectional calendar sync, and enable Jitsi or Google Meet to automatically generate a video link on each booking.",[],{"type":38,"title":83,"body":84,"items":85},"Reverse proxy: nginx and Caddy","Cal.com listens on port 3000 inside the container. A HTTPS reverse proxy is required for two reasons: exposing port 443 and forwarding the correct `Host` header — otherwise `NEXT_PUBLIC_WEBAPP_URL` no longer matches the real origin and authentication redirects break.\n\n**With Caddy** (recommended, automatic certificate):\n\n```\nrdv.yourdomain.com {\n    reverse_proxy calcom:3000\n}\n```\n\nCaddy obtains and renews the Let's Encrypt certificate without additional configuration.\n\n**With nginx**, add this block to your configuration:\n\n```\nserver {\n    listen 443 ssl;\n    server_name rdv.yourdomain.com;\n    ssl_certificate     \u002Fetc\u002Fletsencrypt\u002Flive\u002Frdv.yourdomain.com\u002Ffullchain.pem;\n    ssl_certificate_key \u002Fetc\u002Fletsencrypt\u002Flive\u002Frdv.yourdomain.com\u002Fprivkey.pem;\n    location \u002F {\n        proxy_pass http:\u002F\u002F127.0.0.1:3000;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n    }\n}\n```\n\nIn both cases, the URL must match `NEXT_PUBLIC_WEBAPP_URL` **exactly**: no trailing slash, HTTPS, correct subdomain. A single character difference produces infinite redirects or a blank screen.",[],{"type":38,"title":87,"body":88,"items":89},"Critical first-boot invariants","Three environment variables are permanently locked at Cal.com's **first boot**. Changing them afterwards silently corrupts all stored integrations: the application restarts normally, shows zero errors, but Google Calendar, Zoom and all OAuth connections stop working — their tokens were encrypted with the original key, which is now incompatible. This behaviour is documented in calcom\u002Fdocker \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fcalcom\u002Fdocker\u002Fissues\u002F333\">issue #333\u003C\u002Fa> and calcom\u002Fcal.diy \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fcalcom\u002Fcal.diy\u002Fissues\u002F13290\">issue #13290\u003C\u002Fa>: users have lost all their integrations after an update that regenerated the `.env` file.\n\n**`CALENDSO_ENCRYPTION_KEY`** — encrypts the OAuth tokens stored in the database. Any change silently invalidates all existing integrations.\n\n**`NEXTAUTH_URL`** — anchors session cookies. A change breaks authentication for all active users.\n\n**`NEXT_PUBLIC_WEBAPP_URL`** — baked into the Next.js build at compile time. Changing this value requires a full rebuild and reconnecting all integrations.\n\nBest practice: copy these three values into a secrets manager (Bitwarden, HashiCorp Vault, Ansible Vault) as soon as they are generated. If you manage your VPS as infrastructure-as-code, store them in an encrypted vault — never let the `.env` file be their only location.",[],{"type":57,"title":91,"steps":92,"items":105},"Updating Cal.com safely",[93,96,99,102],{"title":94,"body":95},"Back up the PostgreSQL database","Before any update: `docker exec cal-docker-db-1 pg_dump -U calcom calcom | gzip > \u002Fopt\u002Fbackup\u002Fcalcom-$(date +%Y%m%d).sql.gz`. Prisma migrations are not reversible — this backup is your only safety net.",{"title":97,"body":98},"Verify that CALENDSO_ENCRYPTION_KEY is unchanged","Compare the value in your `.env` with the one stored in your secrets manager. If they differ, **stop here**: restore the original value before proceeding. A different key will silently destroy all your OAuth integrations on restart.",{"title":100,"body":101},"Pull the new image and restart","Update with `docker compose pull && docker compose up -d`. Follow the startup with `docker compose logs -f calcom` — wait for the message indicating the server is ready before testing.",{"title":103,"body":104},"Verify integrations after the update","Open Settings → Integrations and confirm that every existing connection (Google Calendar, Zoom, etc.) is still active. A \"not connected\" status means the key changed between restarts — restore the backup and the original `.env`.",[],{"type":38,"title":107,"body":108,"items":109},"Troubleshooting: CLIENT_FETCH_ERROR and other Docker errors","**CLIENT_FETCH_ERROR on page load** — Since early 2026, all new Docker installations encounter this error on first load. Cause: the Next.js container tries to resolve `NEXTAUTH_URL` (your public domain) from inside the Docker network, where external DNS is not reachable. Fix: add `NEXTAUTH_URL_INTERNAL=http:\u002F\u002Fcalcom:3000` to your `.env` and restart with `docker compose up -d`. This variable forces NextAuth to call its own API internally, without going through the public domain. Documented in calcom\u002Fcal.diy \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fcalcom\u002Fcal.diy\u002Fissues\u002F27668\">issue #27668\u003C\u002Fa> (February 2026, 8 comments confirming the error on all recent Docker distributions).\n\n**Integrations lost after an update** — The cause is almost always a `CALENDSO_ENCRYPTION_KEY` that differs between two startups. Check that your `.env` was not overwritten by an `.env.example` during the pull. Compare the current value with the one in your secrets manager. If the values differ: restore the PostgreSQL backup, put the original key back in `.env` and restart with `docker compose up -d`.\n\n**Build \"JavaScript heap out of memory\"** — The Next.js compiler is running out of RAM. Fix: `fallocate -l 2G \u002Fswapfile && mkswap \u002Fswapfile && swapon \u002Fswapfile`, then run `docker compose build` again. On a 2 GB VPS, swap is often essential for the first build and major updates. Remove the swap file once the build is done.\n\n**Redirect loop or \"Unable to find valid origin\"** — `NEXT_PUBLIC_WEBAPP_URL` does not match the actual URL. Check that this variable is exactly `https:\u002F\u002Frdv.yourdomain.com` (no trailing slash, correct domain, HTTPS protocol), that the reverse proxy forwards the `Host` header correctly, then restart with `docker compose up -d --build` to force a rebuild with the correct URL.\n\n**Prisma migrations stuck on startup** — If the container restarts in a loop with migration errors, the database may not yet be ready. Wait a few seconds and run `docker compose restart calcom`. If the problem persists, check the PostgreSQL logs with `docker compose logs db`.",[],{"type":111,"body":112,"items":113},"tip","Automate your PostgreSQL backup with a daily cron job. Example command to schedule: `docker exec cal-docker-db-1 pg_dump -U calcom calcom | gzip > \u002Fopt\u002Fbackup\u002Fcalcom-$(date +%Y%m%d-%H%M).sql.gz`. Keep at least 7 days of rotating backups and ship them to object storage (S3-compatible, Backblaze B2): in case of VPS loss, the PostgreSQL database is the only non-reproducible part of your Cal.com instance.",[],{"type":38,"title":115,"body":116,"items":117},"Going further","Once Cal.com is running, several steps to harden the installation: enable alerts on SSL certificates to anticipate expirations, set up HTTP monitoring (Uptime Kuma, Better Uptime) on `https:\u002F\u002Frdv.yourdomain.com\u002Fapi\u002Fhealth`, and restrict the container's port 3000 to `127.0.0.1` so it is no longer directly reachable. For team installations, the reverse proxy can serve multiple Cal.com instances behind distinct subdomains from a single VPS — see \u003Ca href=\"\u002Fblog\u002Fdeployer-avec-coolify\">deploying with Coolify\u003C\u002Fa> for simplified multi-service management. The official Cal.com documentation also covers SMTP configuration for confirmation emails and Stripe integration for paid bookings.",[],"Host your appointment scheduling","The ServOrbit Cloud VPS provides the RAM and Docker required to build Cal.com, with a PostgreSQL and reverse proxy template ready to configure. Offer your clients a booking link under your own domain.","Deploy on Cloud VPS","\u002Fvps-cloud",[123,146,167],{"id":124,"slug":125,"slugs":126,"title":130,"excerpt":131,"readTime":132,"views":18,"isPinned":19,"noindex":19,"publishedAt":133,"updatedAt":134,"category":135,"categories":140,"featuredImage":30,"bgImage":31,"posterImage":142,"relatedSolution":143},41,"deploy-your-applications-with-coolify-on-a-vps",{"fr":127,"en":125,"ar":128,"es":129},"deployer-avec-coolify","نشر-تطبيقاتك-باستخدام-coolify-على-vps","desplegar-aplicaciones-con-coolify-en-un-vps","Deploy Your Applications with Coolify on a VPS","Deploy Coolify on a VPS: installation, HTTPS configuration, Git connection, deploying your first application, databases, backups, and troubleshooting.",12,"2026-05-10T00:00:00+00:00","2026-10-31T01:54:10+00:00",{"id":136,"name":137,"slug":138,"color":139,"icon":138},3,"Deployment","deploiement","bg-success\u002F10 text-success",[141],{"id":136,"name":137,"slug":138,"color":139,"icon":138},"\u002Fblog\u002Fcovers\u002Fdeployer-avec-coolify-poster.svg",{"categorySlug":144,"appSlug":145},"application-deployment-devops","coolify",{"id":147,"slug":148,"slugs":149,"title":153,"excerpt":154,"readTime":155,"views":156,"isPinned":19,"noindex":19,"publishedAt":157,"updatedAt":158,"category":159,"categories":164,"featuredImage":30,"bgImage":31,"posterImage":166,"relatedSolution":30},116,"free-ssl-certificates-with-lets-encrypt-on-a-vps",{"fr":150,"en":148,"ar":151,"es":152},"certificats-ssl-lets-encrypt-vps","شهادات-ssl-مجانية-باستخدام-lets-encrypt-على-خادم-vps","certificados-ssl-gratis-con-lets-encrypt-en-un-vps","Free SSL Certificates with Let's Encrypt on a VPS","Complete Let's Encrypt guide for VPS: certbot nginx, DNS-01 wildcard, systemd renewal, TLS hardening, rate-limit troubleshooting and certbot vs acme.sh vs Caddy.",8,0,"2026-02-24T00:00:00+00:00","2026-09-30T11:20:34+00:00",{"id":155,"name":160,"slug":161,"color":162,"icon":163},"Security & Monitoring","securite-monitoring","bg-rose-500\u002F10 text-rose-400","security",[165],{"id":155,"name":160,"slug":161,"color":162,"icon":163},"\u002Fblog\u002Fcovers\u002Fcertificats-ssl-lets-encrypt-vps-poster.svg",{"id":168,"slug":169,"slugs":170,"title":174,"excerpt":175,"readTime":155,"views":176,"isPinned":19,"noindex":19,"publishedAt":177,"updatedAt":178,"category":179,"categories":180,"featuredImage":30,"bgImage":31,"posterImage":182,"relatedSolution":30},228,"initial-linux-server-hardening",{"fr":171,"en":169,"ar":172,"es":173},"durcissement-serveur-linux-initial","تصليب-الخادم-linux-الأولي","hardening-inicial-servidor-linux","Initial Linux Server Hardening","Create a sudo user, configure SSH with keys, enable UFW and fail2ban on Ubuntu or Debian in under an hour.",1,"2026-08-06T00:00:00+00:00","2026-10-20T23:18:07+00:00",{"id":155,"name":160,"slug":161,"color":162,"icon":163},[181],{"id":155,"name":160,"slug":161,"color":162,"icon":163},"\u002Fblog\u002Fcovers\u002Fdurcissement-serveur-linux-initial-poster.svg",true,1794270408731]