Copy-ready setups for self-hosted Reactive Resume: Caddy, Traefik and nginx with HTTPS, S3 storage, Redis, local AI with Ollama, and more.
Each section on this page solves one common self-hosting task and builds on the two-container setup from
Self-hosting with Docker. Replace resume.example.com with your own domain, and keep the
.env file from that guide unless a section says otherwise.
The whole site goes to the app. The app serves pages, the API (/api/), the MCP server (/mcp), uploads and
assets from one address. Don’t split or rewrite paths.
APP_URL is the public address, for example APP_URL="https://resume.example.com".
Client-supplied IP headers are replaced or removed. The Node server uses its connection’s remote address for
IP-based limits, such as sign-in attempts and public resume passwords, and ignores forwarded IP headers. Behind a
reverse proxy, users share the proxy’s IP-based limit. Vercel uses the client address supplied by its deployment
adapter. The examples below also replace X-Forwarded-For and remove alternate IP headers at the proxy.
Streaming responses aren’t buffered, and idle reads are allowed for at least 5 minutes. Assistant replies and
live updates stream from the server, and a single Assistant reply can take up to 4 minutes.
Request bodies of at least 50 MB are accepted. People can attach files of up to 25 MB to the Assistant, and
the browser sends them base64-encoded, which makes them about a third larger.
Once a proxy is in front, don’t publish the app’s port 3000 on a public interface, or clients can bypass the proxy’s
HTTPS and access controls. Remove the ports entry, or bind it to 127.0.0.1:3000:3000.
Point your domain’s DNS at the server, set APP_URL="https://resume.example.com" in .env, and run
docker compose up -d. Caddy replaces any X-Forwarded-For header a client sends with the real client address, and
the header_up lines drop the other IP headers.
Add ACME_EMAIL="you@example.com" to .env for Let’s Encrypt notices. Traefik skips containers whose health check
fails, so it only routes to the app once /api/health answers 200.This example serves one application hostname. To add a short alias or let users connect their own domains, follow
Sharing domains; custom domains also need DNS verification and the HTTP provider.
With nginx you manage certificates yourself, for example with
Certbot. Add this service to the compose file from the Docker guide, and remove the
ports entry from the reactive-resume service:
If you already run PostgreSQL, or use a managed database, leave out the postgres service and point DATABASE_URL
at your server. Create an empty database and a user that owns it; the app creates its tables on first start.
S3 storage lets you drop the /app/data volume, and it’s required if people attach files in the Assistant. The app
switches to S3 once the access key, secret key and bucket are all set. The bucket can stay private: the app reads
objects with its own credentials and serves them itself.
The bucket must exist before the app starts. Check /api/health: its storage entry should show "type": "s3" and
"status": "healthy". Files already in /app/data aren’t moved; copy them into the bucket, keeping their paths, if you
switch an existing instance.
Personal AI providers need ENCRYPTION_SECRET; shared AI configuration does not.
Redis is optional on a single server, but with it an Assistant reply keeps
streaming after a page reload. Add a Redis service to your compose file:
To keep AI requests on your own hardware, run Ollama next to the app. By default the app only
accepts public https:// provider addresses, so you turn on a flag that allows local ones.
FLAG_ALLOW_UNSAFE_AI_BASE_URL lets every user make your server send requests to any address on your network. Only
turn it on for an instance where you trust every user, such as a personal or family server.
For a personal or team server that nobody else can join:
Create your own account (and your team’s) first.
Add FLAG_DISABLE_SIGNUPS="true" to .env and run docker compose up -d.
New sign-ups are then refused, by email and by social sign-in. People who already have accounts sign in as usual.To allow sign-in only through your company’s identity provider, set up a custom OAuth provider (see
Single sign-on), then add FLAG_DISABLE_EMAIL_AUTH="true". This removes email and password
sign-in, along with password resets.
The ID is the last part of the editor’s address: https://resume.example.com/builder/<resume-id>.
3
Set the variable and restart
.env
ROOT_RESUME_ID="<resume-id>"
Run docker compose up -d.
The resume’s own settings still apply: a password still protects it, and Visitors can download the PDF still
controls the download button. Its usual /<username>/<slug> address keeps working.
Renaming the address or username doesn’t break it; the ID stays the same.
If the resume is deleted or its public link is turned off, / shows an unavailable page, even to you.
Sign-in and your documents stay at their usual addresses. The root page follows the resume’s Show in search engines setting.
Remove the variable, or leave it empty, and restart to bring back the home page.
Deploy it with docker stack deploy -c compose-swarm.yml reactive-resume, next to your PostgreSQL, Redis and S3
services, and route traffic to it with your proxy.
Have a working setup that isn’t covered here, such as Podman, Portainer or Cloudflare Tunnel?
Open a pull request that adds it to this page. Include when
someone would use it, the complete configuration, and the environment variables it needs.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"Reactive Resume","description":"A privacy-minded resume builder that is customizable, portable, open-source, and free to use.","colors":{"primary":"#166534","light":"#07C983","dark":"#15803D"},"logo":{"light":"https://mintcdn.com/reactiveresume/lsEa0V8tmq-7Vofb/logo/light.svg?fit=max\u0026auto=format\u0026n=lsEa0V8tmq-7Vofb\u0026q=85\u0026s=21a2f3c9b27509effa871b216e67c0ce","dark":"https://mintcdn.com/reactiveresume/lsEa0V8tmq-7Vofb/logo/dark.svg?fit=max\u0026auto=format\u0026n=lsEa0V8tmq-7Vofb\u0026q=85\u0026s=fe4e07f4f06411b0ad16fd4644a475de","href":"https://rxresu.me"},"favicon":"https://mintcdn.com/reactiveresume/lsEa0V8tmq-7Vofb/favicon.svg?fit=max\u0026auto=format\u0026n=lsEa0V8tmq-7Vofb\u0026q=85\u0026s=6d7f6f71f71340846e94142b357a89b7","background":{"decoration":"grid"},"navbar":{"links":[{"href":"https://rxresu.me/resume-templates","label":"Resume templates"},{"href":"https://github.com/reactive-resume/reactive-resume/issues","label":"Support"}],"primary":{"type":"button","label":"Build your resume","href":"https://rxresu.me/auth/register"}},"footer":{"socials":{"website":"https://rxresu.me","github":"https://github.com/reactive-resume/reactive-resume"}},"seo":{"metatags":{"canonical":"https://docs.rxresu.me","og:image":"https://rxresu.me/opengraph/banner.jpg"},"indexing":"navigable"},"fonts":{"heading":{"family":"IBM Plex Sans"},"body":{"family":"IBM Plex Sans"}},"icons":{"library":"lucide"},"contextual":{"options":["copy","view"],"display":"header"},"styling":{"latex":true},"navigation":{"global":{"anchors":[{"anchor":"Reactive Resume","icon":"link","href":"https://rxresu.me"},{"anchor":"Source Code","icon":"github","href":"https://github.com/reactive-resume/reactive-resume"}]},"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"reactiveresume","hasAudioNarration":false,"actualSubdomain":"reactiveresume","customDomains":["docs.rxresu.me"],"gitSource":{"type":"github","owner":"reactive-resume","repo":"reactive-resume","deployBranch":"main","contentDirectory":"docs","isPrivate":false,"mountPath":"","label":""},"chroma":{"liveDeploymentHistoryId":"6ae5c56f67c58c3a14fdcf7b","collectionId":"3668b27d-b224-4a70-954a-b9f22bf6f0c4","schemaVersion":"v3","latestBuiltDeploymentHistoryId":"6ae5c56f67c58c3a14fdcf7b"},"feedback":{"edits":true,"thumbs":false,"issues":true,"agentFeedback":true},"entitlements":{"AI_CHAT":{"status":"ENABLED"},"WEB_MCP":{"status":"ENABLED"},"NARRATION":{"status":"ENABLED"}},"buildId":"6ae5dc028478937caf585fc3","clientVersion":"0.0.4043","searchFilterCounts":[{"language":"default","tag":"API Reference","count":221},{"language":"default","tag":"Documentation","count":124},{"language":"default","tag":"Applications","count":42},{"language":"default","tag":"Career","count":36},{"language":"default","tag":"Documents","count":28},{"language":"default","tag":"Resumes","count":26},{"language":"default","tag":"Cover Letters","count":24},{"language":"default","tag":"Comparisons","count":17},{"language":"default","tag":"Agent","count":15},{"language":"default","tag":"Integrations","count":13}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":true,"enableContextualStarterQuestions":true,"enableAskAiSearchBar":true,"deflection":{"enabled":true,"email":"hello@amruthpillai.com","showHelpButton":true}},"basePath":"","hasChatPermissions":true,"hasWebMcpPermissions":true,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"byok-sep-25","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-updated-9-15","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":true},"customCss":[],"page":{"pagePath":"self-hosting/examples","slug":"/self-hosting/examples","pageMetadata":{"title":"Deployment examples","description":"Copy-ready setups for self-hosted Reactive Resume: Caddy, Traefik and nginx with HTTPS, S3 storage, Redis, local AI with Ollama, and more.","href":"/self-hosting/examples"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"Copy-ready setups for self-hosted Reactive Resume: Caddy, Traefik and nginx with HTTPS, S3 storage, Redis, local AI with Ollama, and more.\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,li:`li`,ol:`ol`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,ul:`ul`,..._provideComponents(),...props.components},{CodeBlock,Frame,Heading,Note,OptimizedImage,Step,Steps,Tab,Tabs,Warning}=_components;if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Frame)_missingMdxReference(`Frame`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Note)_missingMdxReference(`Note`,true);if(!OptimizedImage)_missingMdxReference(`OptimizedImage`,true);if(!Step)_missingMdxReference(`Step`,true);if(!Steps)_missingMdxReference(`Steps`,true);if(!Tab)_missingMdxReference(`Tab`,true);if(!Tabs)_missingMdxReference(`Tabs`,true);if(!Warning)_missingMdxReference(`Warning`,true);return _jsxs(_Fragment,{children:[_jsxs(_components.p,{children:[`Each section on this page solves one common self-hosting task and builds on the two-container setup from\n`,_jsx(_components.a,{href:`/self-hosting/docker`,children:`Self-hosting with Docker`}),`. Replace `,_jsx(_components.code,{children:`resume.example.com`}),` with your own domain, and keep the\n`,_jsx(_components.code,{children:`.env`}),` file from that guide unless a section says otherwise.`]}),`\n`,_jsx(Heading,{level:`2`,id:`what-every-reverse-proxy-needs`,children:`What every reverse proxy needs`}),`\n`,_jsx(_components.p,{children:`Whatever proxy you use, configure it so that:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`The whole site goes to the app.`}),` The app serves pages, the API (`,_jsx(_components.code,{children:`/api/`}),`), the MCP server (`,_jsx(_components.code,{children:`/mcp`}),`), uploads and\nassets from one address. Don’t split or rewrite paths.`]}),`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[_jsx(_components.code,{children:`APP_URL`}),` is the public address`]}),`, for example `,_jsx(_components.code,{children:`APP_URL=\"https://resume.example.com\"`}),`.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Client-supplied IP headers are replaced or removed.`}),` The Node server uses its connection’s remote address for\nIP-based limits, such as sign-in attempts and public resume passwords, and ignores forwarded IP headers. Behind a\nreverse proxy, users share the proxy’s IP-based limit. Vercel uses the client address supplied by its deployment\nadapter. The examples below also replace `,_jsx(_components.code,{children:`X-Forwarded-For`}),` and remove alternate IP headers at the proxy.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Streaming responses aren’t buffered`}),`, and idle reads are allowed for at least 5 minutes. Assistant replies and\nlive updates stream from the server, and a single Assistant reply can take up to 4 minutes.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Request bodies of at least 50 MB are accepted.`}),` People can attach files of up to 25 MB to the Assistant, and\nthe browser sends them base64-encoded, which makes them about a third larger.`]}),`\n`]}),`\n`,_jsx(Warning,{children:_jsxs(_components.p,{children:[`Once a proxy is in front, don’t publish the app’s port `,_jsx(_components.code,{children:`3000`}),` on a public interface, or clients can bypass the proxy’s\nHTTPS and access controls. Remove the `,_jsx(_components.code,{children:`ports`}),` entry, or bind it to `,_jsx(_components.code,{children:`127.0.0.1:3000:3000`}),`.`]})}),`\n`,_jsx(Heading,{level:`2`,id:`caddy`,children:`Caddy`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.a,{href:`https://caddyserver.com/`,children:`Caddy`}),` gets and renews HTTPS certificates on its own and streams responses without extra\nsettings, so it needs the least configuration.`]}),`\n`,_jsx(CodeBlock,{filename:`compose.yml`,numberOfLines:`41`,language:`yaml`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`yaml`,children:_jsxs(_components.code,{language:`yaml`,numberOfLines:`41`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`services`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` caddy`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` image`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`caddy:2`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` restart`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`unless-stopped`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` ports`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"80:80\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"443:443\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"443:443/udp\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` volumes`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`./Caddyfile:/etc/caddy/Caddyfile:ro`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`caddy_data:/data`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` postgres`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` image`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`postgres:18`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` restart`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`unless-stopped`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` environment`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` POSTGRES_DB`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`postgres`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` POSTGRES_USER`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`postgres`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` POSTGRES_PASSWORD`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:\"${POSTGRES_PASSWORD}\"})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` volumes`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`postgres_data:/var/lib/postgresql`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` healthcheck`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` test`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: [`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"CMD-SHELL\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"pg_isready -U postgres -d postgres\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`]`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` interval`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`10s`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` timeout`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`5s`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` retries`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`10`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` reactive-resume`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` image`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`amruthpillai/reactive-resume:latest`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` restart`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`unless-stopped`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` env_file`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`.env`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` volumes`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`reactive_resume_data:/app/data`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` depends_on`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` postgres`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` condition`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`service_healthy`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`volumes`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` caddy_data`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` postgres_data`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` reactive_resume_data`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`]})})}),`\n`,_jsx(CodeBlock,{filename:`Caddyfile`,numberOfLines:`11`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`11`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`resume.example.com {`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`\trequest_body {`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`\t\tmax_size 50MB`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`\t}`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`\treverse_proxy reactive-resume:3000 {`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`\t\theader_up -CF-Connecting-IP`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`\t\theader_up -CF-Connecting-IPv6`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`\t\theader_up -True-Client-IP`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`\t}`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`}`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsxs(_components.p,{children:[`Point your domain’s DNS at the server, set `,_jsx(_components.code,{children:`APP_URL=\"https://resume.example.com\"`}),` in `,_jsx(_components.code,{children:`.env`}),`, and run\n`,_jsx(_components.code,{children:`docker compose up -d`}),`. Caddy replaces any `,_jsx(_components.code,{children:`X-Forwarded-For`}),` header a client sends with the real client address, and\nthe `,_jsx(_components.code,{children:`header_up`}),` lines drop the other IP headers.`]}),`\n`,_jsx(Heading,{level:`2`,id:`traefik`,children:`Traefik`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.a,{href:`https://traefik.io/`,children:`Traefik`}),` reads its routes from Docker labels and gets certificates from Let’s Encrypt.`]}),`\n`,_jsx(CodeBlock,{filename:`compose.yml`,lines:`true`,expandable:`true`,numberOfLines:`62`,language:`yaml`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`yaml`,children:_jsxs(_components.code,{language:`yaml`,numberOfLines:`62`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`services`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` traefik`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` image`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`traefik:v3`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` restart`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`unless-stopped`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` command`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"--providers.docker=true\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"--providers.docker.exposedbydefault=false\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"--entrypoints.web.address=:80\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"--entrypoints.web.http.redirections.entrypoint.to=websecure\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"--entrypoints.web.http.redirections.entrypoint.scheme=https\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"--entrypoints.websecure.address=:443\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"--certificatesresolvers.letsencrypt.acme.httpchallenge=true\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:'\"--certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL}\"'})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` ports`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"80:80\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"443:443\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` volumes`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`/var/run/docker.sock:/var/run/docker.sock:ro`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`traefik_letsencrypt:/letsencrypt`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` postgres`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` image`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`postgres:18`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` restart`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`unless-stopped`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` environment`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` POSTGRES_DB`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`postgres`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` POSTGRES_USER`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`postgres`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` POSTGRES_PASSWORD`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:\"${POSTGRES_PASSWORD}\"})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` volumes`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`postgres_data:/var/lib/postgresql`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` healthcheck`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` test`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: [`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"CMD-SHELL\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"pg_isready -U postgres -d postgres\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`]`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` interval`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`10s`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` timeout`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`5s`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` retries`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`10`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` reactive-resume`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` image`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`amruthpillai/reactive-resume:latest`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` restart`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`unless-stopped`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` env_file`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`.env`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` volumes`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`reactive_resume_data:/app/data`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` depends_on`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` postgres`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` condition`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`service_healthy`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` labels`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"traefik.enable=true\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:'\"traefik.http.routers.reactive-resume.rule=Host(`resume.example.com`)\"'})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"traefik.http.routers.reactive-resume.entrypoints=websecure\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"traefik.http.routers.reactive-resume.tls.certresolver=letsencrypt\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"traefik.http.services.reactive-resume.loadbalancer.server.port=3000\"`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` # Drop client-supplied IP headers; Traefik sets X-Forwarded-For itself.`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"traefik.http.middlewares.reactive-resume-ip.headers.customrequestheaders.CF-Connecting-IP=\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"traefik.http.middlewares.reactive-resume-ip.headers.customrequestheaders.CF-Connecting-IPv6=\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"traefik.http.middlewares.reactive-resume-ip.headers.customrequestheaders.True-Client-IP=\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"traefik.http.routers.reactive-resume.middlewares=reactive-resume-ip\"`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`volumes`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` traefik_letsencrypt`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` postgres_data`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` reactive_resume_data`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`Add `,_jsx(_components.code,{children:`ACME_EMAIL=\"you@example.com\"`}),` to `,_jsx(_components.code,{children:`.env`}),` for Let’s Encrypt notices. Traefik skips containers whose health check\nfails, so it only routes to the app once `,_jsx(_components.code,{children:`/api/health`}),` answers `,_jsx(_components.code,{children:`200`}),`.`]}),`\n`,_jsxs(_components.p,{children:[`This example serves one application hostname. To add a short alias or let users connect their own domains, follow\n`,_jsx(_components.a,{href:`/self-hosting/sharing-domains`,children:`Sharing domains`}),`; custom domains also need DNS verification and the HTTP provider.`]}),`\n`,_jsx(Heading,{level:`2`,id:`nginx`,children:`nginx`}),`\n`,_jsxs(_components.p,{children:[`With `,_jsx(_components.a,{href:`https://nginx.org/`,children:`nginx`}),` you manage certificates yourself, for example with\n`,_jsx(_components.a,{href:`https://certbot.eff.org/`,children:`Certbot`}),`. Add this service to the compose file from the Docker guide, and remove the\n`,_jsx(_components.code,{children:`ports`}),` entry from the `,_jsx(_components.code,{children:`reactive-resume`}),` service:`]}),`\n`,_jsx(CodeBlock,{filename:`compose.yml`,numberOfLines:`11`,language:`yaml`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`yaml`,children:_jsxs(_components.code,{language:`yaml`,numberOfLines:`11`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`nginx`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` image`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`nginx:stable-alpine`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` restart`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`unless-stopped`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` ports`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"80:80\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"443:443\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` volumes`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`./nginx.conf:/etc/nginx/conf.d/default.conf:ro`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`./certs:/etc/nginx/certs:ro`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` depends_on`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`reactive-resume`})]}),`\n`]})})}),`\n`,_jsx(CodeBlock,{filename:`nginx.conf`,lines:`true`,expandable:`true`,numberOfLines:`39`,language:`nginx`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`nginx`,children:_jsxs(_components.code,{language:`nginx`,numberOfLines:`39`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`server`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` listen `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`80`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` server_name `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`resume.example.com;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:` return`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:` 301`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` https://$`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`host`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`$`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`request_uri`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`}`})}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`server`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` listen `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`443`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` ssl;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` http2 `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:`on`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` server_name `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`resume.example.com;`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` ssl_certificate `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` /etc/nginx/certs/fullchain.pem;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` ssl_certificate_key `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`/etc/nginx/certs/privkey.pem;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` ssl_protocols `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` TLSv1.2 TLSv1.3;`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` # Assistant attachments can be up to 25 MB, sent base64-encoded.`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` client_max_body_size `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`50m`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` location`}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#D4D4D4`},children:` / `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`{`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` proxy_pass `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`http://reactive-resume:3000;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` proxy_http_version `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`1.1`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` proxy_set_header `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`Host $`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`host`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` proxy_set_header `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`X-Real-IP $`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`remote_addr`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` proxy_set_header `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`X-Forwarded-For $`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`remote_addr`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` proxy_set_header `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`X-Forwarded-Proto $`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`scheme`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` # Don't pass client-supplied IP headers on (an empty value removes them).`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` proxy_set_header `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`CF-Connecting-IP `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` proxy_set_header `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`CF-Connecting-IPv6 `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` proxy_set_header `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`True-Client-IP `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` # Assistant replies and live updates stream; don't hold them back.`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` proxy_buffering `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:`off`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` proxy_cache `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:`off`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` proxy_read_timeout `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`300s`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` proxy_send_timeout `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`300s`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` }`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`}`})}),`\n`]})})}),`\n`,_jsx(Heading,{level:`2`,id:`use-an-existing-postgresql-server`,children:`Use an existing PostgreSQL server`}),`\n`,_jsxs(_components.p,{children:[`If you already run PostgreSQL, or use a managed database, leave out the `,_jsx(_components.code,{children:`postgres`}),` service and point `,_jsx(_components.code,{children:`DATABASE_URL`}),`\nat your server. Create an empty database and a user that owns it; the app creates its tables on first start.`]}),`\n`,_jsx(CodeBlock,{filename:`.env`,numberOfLines:`1`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`1`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`DATABASE_URL`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"postgresql://reactive_resume:password@db.example.com:5432/reactive_resume?sslmode=verify-full\"`})]}),`\n`]})})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Use `,_jsx(_components.code,{children:`sslmode=verify-full`}),` (or your provider’s equivalent) whenever the connection leaves the host, so the app checks\nthe server’s certificate.`]}),`\n`,_jsxs(_components.li,{children:[`If `,_jsx(_components.code,{children:`DATABASE_URL`}),` points at a connection pooler, add a direct connection in `,_jsx(_components.code,{children:`DATABASE_MIGRATION_URL`}),` for startup\nmigrations.`]}),`\n`,_jsx(_components.li,{children:`Never expose PostgreSQL to the internet without TLS and a firewall.`}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`store-uploads-in-s3-compatible-storage`,children:`Store uploads in S3-compatible storage`}),`\n`,_jsxs(_components.p,{children:[`S3 storage lets you drop the `,_jsx(_components.code,{children:`/app/data`}),` volume, and it’s required if people attach files in the Assistant. The app\nswitches to S3 once the access key, secret key and bucket are all set. The bucket can stay private: the app reads\nobjects with its own credentials and serves them itself.`]}),`\n`,_jsxs(Tabs,{children:[_jsx(Tab,{title:`AWS S3`,id:`aws-s3`,children:_jsx(CodeBlock,{filename:`.env`,numberOfLines:`4`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`4`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`S3_ACCESS_KEY_ID`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"AKIA...\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`S3_SECRET_ACCESS_KEY`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"...\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`S3_BUCKET`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"my-reactive-resume\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`S3_REGION`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"eu-central-1\"`})]}),`\n`]})})})}),_jsx(Tab,{title:`Cloudflare R2`,id:`cloudflare-r2`,children:_jsx(CodeBlock,{filename:`.env`,numberOfLines:`5`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`5`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`S3_ACCESS_KEY_ID`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"...\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`S3_SECRET_ACCESS_KEY`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"...\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`S3_BUCKET`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"reactive-resume\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`S3_REGION`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"auto\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`S3_ENDPOINT`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"https://\u003caccount-id>.r2.cloudflarestorage.com\"`})]}),`\n`]})})})}),_jsx(Tab,{title:`MinIO or SeaweedFS`,id:`minio-or-seaweedfs`,children:_jsx(CodeBlock,{filename:`.env`,numberOfLines:`5`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`5`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`S3_ACCESS_KEY_ID`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"...\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`S3_SECRET_ACCESS_KEY`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"...\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`S3_BUCKET`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"reactive-resume\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`S3_ENDPOINT`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"http://minio:9000\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`S3_FORCE_PATH_STYLE`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"true\"`})]}),`\n`]})})})})]}),`\n`,_jsxs(_components.p,{children:[`The bucket must exist before the app starts. Check `,_jsx(_components.code,{children:`/api/health`}),`: its `,_jsx(_components.code,{children:`storage`}),` entry should show `,_jsx(_components.code,{children:`\"type\": \"s3\"`}),` and\n`,_jsx(_components.code,{children:`\"status\": \"healthy\"`}),`. Files already in `,_jsx(_components.code,{children:`/app/data`}),` aren’t moved; copy them into the bucket, keeping their paths, if you\nswitch an existing instance.`]}),`\n`,_jsx(Heading,{level:`2`,id:`add-redis-and-turn-on-ai`,children:`Add Redis and turn on AI`}),`\n`,_jsxs(_components.p,{children:[`Personal AI providers need `,_jsx(_components.code,{children:`ENCRYPTION_SECRET`}),`; `,_jsx(_components.a,{href:`/self-hosting/job-search-and-ai`,children:`shared AI configuration`}),` does not.\nRedis is optional on a single server, but with it an Assistant reply keeps\nstreaming after a page reload. Add a Redis service to your compose file:`]}),`\n`,_jsx(CodeBlock,{filename:`compose.yml`,numberOfLines:`11`,language:`yaml`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`yaml`,children:_jsxs(_components.code,{language:`yaml`,numberOfLines:`11`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`redis`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` image`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`redis:8`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` restart`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`unless-stopped`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` command`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`redis-server --appendonly yes`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` volumes`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`redis_data:/data`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` healthcheck`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` test`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: [`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"CMD\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"redis-cli\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"ping\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`]`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` interval`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`10s`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` timeout`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`5s`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` retries`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`10`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`Add `,_jsx(_components.code,{children:`redis_data:`}),` under `,_jsx(_components.code,{children:`volumes:`}),`, and add `,_jsx(_components.code,{children:`redis`}),` to the app’s `,_jsx(_components.code,{children:`depends_on`}),` with `,_jsx(_components.code,{children:`condition: service_healthy`}),`. Then\nadd to `,_jsx(_components.code,{children:`.env`}),`:`]}),`\n`,_jsx(CodeBlock,{filename:`.env`,numberOfLines:`3`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`3`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# Generate with: openssl rand -hex 32. Keep it different from AUTH_SECRET.`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`ENCRYPTION_SECRET`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"...\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`REDIS_URL`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"redis://redis:6379\"`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`Run `,_jsx(_components.code,{children:`docker compose up -d`}),`. People can now add their own AI provider; see `,_jsx(_components.a,{href:`/guides/using-ai`,children:`Connecting an AI provider`}),`\nand `,_jsx(_components.a,{href:`/guides/using-the-assistant`,children:`Using the Assistant`}),`.`]}),`\n`,_jsx(Note,{children:_jsx(_components.p,{children:`Redis holds streaming Assistant replies, which include people’s messages. Keep it on the private Docker network and\ndon’t publish its port.`})}),`\n`,_jsx(Heading,{level:`2`,id:`use-a-local-ai-model-with-ollama`,children:`Use a local AI model with Ollama`}),`\n`,_jsxs(_components.p,{children:[`To keep AI requests on your own hardware, run `,_jsx(_components.a,{href:`https://ollama.com/`,children:`Ollama`}),` next to the app. By default the app only\naccepts public `,_jsx(_components.code,{children:`https://`}),` provider addresses, so you turn on a flag that allows local ones.`]}),`\n`,_jsx(Warning,{children:_jsxs(_components.p,{children:[_jsx(_components.code,{children:`FLAG_ALLOW_UNSAFE_AI_BASE_URL`}),` lets every user make your server send requests to any address on your network. Only\nturn it on for an instance where you trust every user, such as a personal or family server.`]})}),`\n`,_jsxs(Steps,{children:[_jsxs(Step,{title:`Add Ollama to compose.yml`,children:[_jsx(CodeBlock,{filename:`compose.yml`,numberOfLines:`5`,language:`yaml`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`yaml`,children:_jsxs(_components.code,{language:`yaml`,numberOfLines:`5`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` ollama`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` image`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`ollama/ollama`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` restart`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`unless-stopped`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` volumes`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`ollama_data:/root/.ollama`})]}),`\n`]})})}),_jsxs(_components.p,{children:[`Add `,_jsx(_components.code,{children:`ollama_data:`}),` under `,_jsx(_components.code,{children:`volumes:`}),`, start it, and download a model:`]}),_jsx(CodeBlock,{filename:``,numberOfLines:`2`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`2`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`docker`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` compose`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` up`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -d`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` ollama`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`docker`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` compose`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` exec`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` ollama`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` ollama`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` pull`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` llama3.1`})]}),`\n`]})})})]}),_jsxs(Step,{title:`Allow local AI addresses`,children:[_jsxs(_components.p,{children:[`Add to `,_jsx(_components.code,{children:`.env`}),`, next to the `,_jsx(_components.code,{children:`ENCRYPTION_SECRET`}),` from the previous section:`]}),_jsx(CodeBlock,{filename:`.env`,numberOfLines:`3`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`3`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`FLAG_ALLOW_UNSAFE_AI_BASE_URL`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"true\"`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# Local models can take a while to load on first use.`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`AI_TEST_TIMEOUT_MS`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"120000\"`})]}),`\n`]})})}),_jsxs(_components.p,{children:[`Run `,_jsx(_components.code,{children:`docker compose up -d`}),` to apply it.`]})]}),_jsxs(Step,{title:`Add the provider in Reactive Resume`,children:[_jsxs(_components.p,{children:[`Open `,_jsx(_components.strong,{children:`Settings`}),`, then `,_jsx(_components.strong,{children:`AI \u0026 developer`}),`, and select `,_jsx(_components.strong,{children:`Add provider`}),`. Choose `,_jsx(_components.strong,{children:`Ollama`}),` as the provider and\nfill in:`]}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`API key`}),`: leave empty for a local Ollama server that doesn’t require authentication.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Model`}),`: the model you pulled, for example `,_jsx(_components.code,{children:`llama3.1`}),`.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Base URL`}),`: `,_jsx(_components.code,{children:`http://ollama:11434/api`})]}),`\n`]}),_jsxs(_components.p,{children:[`Select `,_jsx(_components.strong,{children:`Save and test`}),`. The app saves the provider once Ollama answers.`]}),_jsx(Frame,{caption:`The Add provider dialog set up for an Ollama container on the same Docker network`,children:_jsx(OptimizedImage,{src:`https://mintcdn.com/reactiveresume/rtb73NdrPCUreUAd/images/self-hosting/examples/add-provider-local-ollama.webp?fit=max\u0026auto=format\u0026n=rtb73NdrPCUreUAd\u0026q=85\u0026s=8eae632786d34f952ecd9b836de617d8`,alt:`Add provider dialog with Ollama Cloud selected, model llama3.1, name Home server Ollama and base URL http://ollama:11434/api`,\"data-og-width\":`972`,width:`972`,\"data-og-height\":`940`,height:`940`,\"data-path\":`images/self-hosting/examples/add-provider-local-ollama.webp`,\"data-optimize\":`true`,\"data-opv\":`3`,srcset:`https://mintcdn.com/reactiveresume/rtb73NdrPCUreUAd/images/self-hosting/examples/add-provider-local-ollama.webp?w=280\u0026fit=max\u0026auto=format\u0026n=rtb73NdrPCUreUAd\u0026q=85\u0026s=bb8e8abb5824e2a4cd8962dcd8efb4b6 280w, https://mintcdn.com/reactiveresume/rtb73NdrPCUreUAd/images/self-hosting/examples/add-provider-local-ollama.webp?w=560\u0026fit=max\u0026auto=format\u0026n=rtb73NdrPCUreUAd\u0026q=85\u0026s=c87bda17c0880ccb601f7ca3ca4a5cd3 560w, https://mintcdn.com/reactiveresume/rtb73NdrPCUreUAd/images/self-hosting/examples/add-provider-local-ollama.webp?w=840\u0026fit=max\u0026auto=format\u0026n=rtb73NdrPCUreUAd\u0026q=85\u0026s=b2c21edafde2c75d4c758a1324946d81 840w, https://mintcdn.com/reactiveresume/rtb73NdrPCUreUAd/images/self-hosting/examples/add-provider-local-ollama.webp?w=1100\u0026fit=max\u0026auto=format\u0026n=rtb73NdrPCUreUAd\u0026q=85\u0026s=55a5ebe3bca4c695b40d779dfb182680 1100w, https://mintcdn.com/reactiveresume/rtb73NdrPCUreUAd/images/self-hosting/examples/add-provider-local-ollama.webp?w=1650\u0026fit=max\u0026auto=format\u0026n=rtb73NdrPCUreUAd\u0026q=85\u0026s=7ad8e1663c8eca909956f5e3d8f62ccd 1650w, https://mintcdn.com/reactiveresume/rtb73NdrPCUreUAd/images/self-hosting/examples/add-provider-local-ollama.webp?w=2500\u0026fit=max\u0026auto=format\u0026n=rtb73NdrPCUreUAd\u0026q=85\u0026s=d7ed0b7b61e1f89b50ee96d073c93794 2500w`})})]})]}),`\n`,_jsxs(_components.p,{children:[`Any server with an OpenAI-compatible API works the same way: choose `,_jsx(_components.strong,{children:`OpenAI-compatible`}),` and enter its address, such\nas `,_jsx(_components.code,{children:`http://llama-server:8080/v1`}),`.`]}),`\n`,_jsx(Heading,{level:`2`,id:`run-a-private-instance`,children:`Run a private instance`}),`\n`,_jsx(_components.p,{children:`For a personal or team server that nobody else can join:`}),`\n`,_jsxs(_components.ol,{children:[`\n`,_jsx(_components.li,{children:`Create your own account (and your team’s) first.`}),`\n`,_jsxs(_components.li,{children:[`Add `,_jsx(_components.code,{children:`FLAG_DISABLE_SIGNUPS=\"true\"`}),` to `,_jsx(_components.code,{children:`.env`}),` and run `,_jsx(_components.code,{children:`docker compose up -d`}),`.`]}),`\n`]}),`\n`,_jsx(_components.p,{children:`New sign-ups are then refused, by email and by social sign-in. People who already have accounts sign in as usual.`}),`\n`,_jsxs(_components.p,{children:[`To allow sign-in only through your company’s identity provider, set up a custom OAuth provider (see\n`,_jsx(_components.a,{href:`/self-hosting/sso`,children:`Single sign-on`}),`), then add `,_jsx(_components.code,{children:`FLAG_DISABLE_EMAIL_AUTH=\"true\"`}),`. This removes email and password\nsign-in, along with password resets.`]}),`\n`,_jsx(Heading,{level:`2`,id:`show-one-resume-at-your-root-address`,children:`Show one resume at your root address`}),`\n`,_jsxs(_components.p,{children:[`To use your instance as a personal resume site, set `,_jsx(_components.code,{children:`ROOT_RESUME_ID`}),`. Visitors to `,_jsx(_components.code,{children:`/`}),` then see that resume’s public\npage instead of the home page.`]}),`\n`,_jsxs(Steps,{children:[_jsx(Step,{title:`Make the resume public`,children:_jsxs(_components.p,{children:[`In the editor, select `,_jsx(_components.strong,{children:`Share`}),`, then `,_jsx(_components.strong,{children:`Publish`}),`. See\n`,_jsx(_components.a,{href:`/guides/sharing-your-resume-publicly`,children:`Sharing your resume publicly`}),`.`]})}),_jsx(Step,{title:`Copy the resume's ID`,children:_jsxs(_components.p,{children:[`The ID is the last part of the editor’s address: `,_jsx(_components.code,{children:`https://resume.example.com/builder/\u003cresume-id>`}),`.`]})}),_jsxs(Step,{title:`Set the variable and restart`,children:[_jsx(CodeBlock,{filename:`.env`,numberOfLines:`1`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`1`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`ROOT_RESUME_ID`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"\u003cresume-id>\"`})]}),`\n`]})})}),_jsxs(_components.p,{children:[`Run `,_jsx(_components.code,{children:`docker compose up -d`}),`.`]})]})]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`The resume’s own settings still apply: a password still protects it, and `,_jsx(_components.strong,{children:`Visitors can download the PDF`}),` still\ncontrols the download button. Its usual `,_jsx(_components.code,{children:`/\u003cusername>/\u003cslug>`}),` address keeps working.`]}),`\n`,_jsx(_components.li,{children:`Renaming the address or username doesn’t break it; the ID stays the same.`}),`\n`,_jsxs(_components.li,{children:[`If the resume is deleted or its public link is turned off, `,_jsx(_components.code,{children:`/`}),` shows an unavailable page, even to you.`]}),`\n`,_jsxs(_components.li,{children:[`Sign-in and your documents stay at their usual addresses. The root page follows the resume’s `,_jsx(_components.strong,{children:`Show in search engines`}),` setting.`]}),`\n`]}),`\n`,_jsx(_components.p,{children:`Remove the variable, or leave it empty, and restart to bring back the home page.`}),`\n`,_jsx(Heading,{level:`2`,id:`run-several-app-containers`,children:`Run several app containers`}),`\n`,_jsx(_components.p,{children:`To run more than one app container, for high availability or Docker Swarm, every container must share state:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:_jsxs(_components.strong,{children:[`The same database, `,_jsx(_components.code,{children:`AUTH_SECRET`}),` and `,_jsx(_components.code,{children:`ENCRYPTION_SECRET`}),`.`]})}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Redis`}),`, set in `,_jsx(_components.code,{children:`REDIS_URL`}),`, so rate limits, stopping an Assistant reply and live updates work across containers.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`S3-compatible storage`}),` instead of `,_jsx(_components.code,{children:`/app/data`}),`, so every container sees the same uploads.`]}),`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[`The same `,_jsx(_components.code,{children:`DEPLOYMENT_NAMESPACE`})]}),` (the default, `,_jsx(_components.code,{children:`default`}),`, is fine), or leave it unset everywhere.`]}),`\n`]}),`\n`,_jsx(_components.p,{children:`Migrations are safe to start in parallel: containers wait for each other, and only one applies changes. A Docker\nSwarm service then looks like this:`}),`\n`,_jsx(CodeBlock,{filename:`compose-swarm.yml`,numberOfLines:`15`,language:`yaml`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`yaml`,children:_jsxs(_components.code,{language:`yaml`,numberOfLines:`15`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`services`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` reactive-resume`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` image`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`amruthpillai/reactive-resume:v6`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` env_file`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`.env`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` networks`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`reactive_resume`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` deploy`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` replicas`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`2`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` update_config`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` order`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`start-first`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` failure_action`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`rollback`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`networks`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` reactive_resume`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` driver`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`overlay`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`Deploy it with `,_jsx(_components.code,{children:`docker stack deploy -c compose-swarm.yml reactive-resume`}),`, next to your PostgreSQL, Redis and S3\nservices, and route traffic to it with your proxy.`]}),`\n`,_jsx(Heading,{level:`2`,id:`share-your-setup`,children:`Share your setup`}),`\n`,_jsxs(_components.p,{children:[`Have a working setup that isn’t covered here, such as Podman, Portainer or Cloudflare Tunnel?\n`,_jsx(_components.a,{href:`https://github.com/reactive-resume/reactive-resume`,children:`Open a pull request`}),` that adds it to this page. Include when\nsomeone would use it, the complete configuration, and the environment variables it needs.`]}),`\n`,_jsx(Heading,{level:`2`,id:`related-pages`,children:`Related pages`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.a,{href:`/self-hosting/environment-variables`,children:`Environment variables`}),`: every setting, with defaults.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.a,{href:`/self-hosting/job-search-and-ai`,children:`Job search and AI`}),`: connect Firecrawl, optional SearXNG and an AI provider.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.a,{href:`/self-hosting/kubernetes`,children:`Self-hosting with Kubernetes`}),`: the same setup as Kubernetes manifests.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.a,{href:`/self-hosting/sso`,children:`Single sign-on`}),`: Google, GitHub, LinkedIn and custom OAuth providers.`]}),`\n`]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Deployment examples","description":"Copy-ready setups for self-hosted Reactive Resume: Caddy, Traefik and nginx with HTTPS, S3 storage, Redis, local AI with Ollama, and more.","href":"/self-hosting/examples"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"What every reverse proxy needs","slug":"what-every-reverse-proxy-needs","depth":2,"children":[]},{"title":"Caddy","slug":"caddy","depth":2,"children":[]},{"title":"Traefik","slug":"traefik","depth":2,"children":[]},{"title":"nginx","slug":"nginx","depth":2,"children":[]},{"title":"Use an existing PostgreSQL server","slug":"use-an-existing-postgresql-server","depth":2,"children":[]},{"title":"Store uploads in S3-compatible storage","slug":"store-uploads-in-s3-compatible-storage","depth":2,"children":[]},{"title":"Add Redis and turn on AI","slug":"add-redis-and-turn-on-ai","depth":2,"children":[]},{"title":"Use a local AI model with Ollama","slug":"use-a-local-ai-model-with-ollama","depth":2,"children":[]},{"title":"Run a private instance","slug":"run-a-private-instance","depth":2,"children":[]},{"title":"Show one resume at your root address","slug":"show-one-resume-at-your-root-address","depth":2,"children":[]},{"title":"Run several app containers","slug":"run-several-app-containers","depth":2,"children":[]},{"title":"Share your setup","slug":"share-your-setup","depth":2,"children":[]},{"title":"Related pages","slug":"related-pages","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"subdomain":"reactiveresume","siteName":"Reactive Resume","title":"Deployment examples - Reactive Resume","contextualStarterQuestions":[{"_id":"6adfe0a6bd3d50684641e1e2","deploymentId":"696550c78205dc150ed83107","questionText":"How do I configure Caddy for HTTPS?","status":"active","generatedBy":"contextual","path":"self-hosting/examples","createdAt":"2026-10-26T23:22:14.886Z","updatedAt":"2026-10-26T23:22:14.886Z","__v":0},{"_id":"6adfe0a6bd3d50684641e1e0","deploymentId":"696550c78205dc150ed83107","questionText":"How do I set up a reverse proxy?","status":"active","generatedBy":"contextual","path":"self-hosting/examples","createdAt":"2026-10-26T23:22:14.886Z","updatedAt":"2026-10-26T23:22:14.886Z","__v":0},{"_id":"6adfe0a6bd3d50684641e1e1","deploymentId":"696550c78205dc150ed83107","questionText":"What are the proxy requirements?","status":"active","generatedBy":"contextual","path":"self-hosting/examples","createdAt":"2026-10-26T23:22:14.886Z","updatedAt":"2026-10-26T23:22:14.886Z","__v":0}],"navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-10-26T23:22:07.565Z","originalFileLocation":"self-hosting/examples.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Copy-ready setups for self-hosted Reactive Resume: Caddy, Traefik and nginx with HTTPS, S3 storage, Redis, local AI with Ollama, and more.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Reactive Resume\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/reactiveresume/m9TtW-C9R_eefHpI/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/reactiveresume/m9TtW-C9R_eefHpI/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/reactiveresume/m9TtW-C9R_eefHpI/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/reactiveresume/m9TtW-C9R_eefHpI/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/reactiveresume/m9TtW-C9R_eefHpI/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/reactiveresume/m9TtW-C9R_eefHpI/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/reactiveresume/m9TtW-C9R_eefHpI/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/reactiveresume/m9TtW-C9R_eefHpI/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/reactiveresume/m9TtW-C9R_eefHpI/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Deployment examples - Reactive Resume\">\u003cmeta data-mint-head property=\"og:description\" content=\"Copy-ready setups for self-hosted Reactive Resume: Caddy, Traefik and nginx with HTTPS, S3 storage, Redis, local AI with Ollama, and more.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Reactive Resume\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.rxresu.me/self-hosting/examples\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://rxresu.me/opengraph/banner.jpg\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Deployment examples - Reactive Resume\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Copy-ready setups for self-hosted Reactive Resume: Caddy, Traefik and nginx with HTTPS, S3 storage, Redis, local AI with Ollama, and more.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://rxresu.me/opengraph/banner.jpg\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/reactiveresume/m9TtW-C9R_eefHpI/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Reactive Resume\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#166534\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.rxresu.me/self-hosting/examples\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/self-hosting/examples.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.rxresu.me/#organization\",\"name\":\"Reactive Resume\",\"url\":\"https://docs.rxresu.me\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/reactiveresume/lsEa0V8tmq-7Vofb/logo/light.svg?fit=max\u0026auto=format\u0026n=lsEa0V8tmq-7Vofb\u0026q=85\u0026s=21a2f3c9b27509effa871b216e67c0ce\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.rxresu.me#website\",\"name\":\"Reactive Resume\",\"url\":\"https://docs.rxresu.me\",\"publisher\":{\"@id\":\"https://docs.rxresu.me/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.rxresu.me/self-hosting/examples#webpage\",\"url\":\"https://docs.rxresu.me/self-hosting/examples\",\"name\":\"Deployment examples\",\"description\":\"Copy-ready setups for self-hosted Reactive Resume: Caddy, Traefik and nginx with HTTPS, S3 storage, Redis, local AI with Ollama, and more.\",\"dateModified\":\"2026-10-26T23:22:07.565Z\",\"isPartOf\":{\"@id\":\"https://docs.rxresu.me#website\"},\"breadcrumb\":{\"@id\":\"https://docs.rxresu.me/self-hosting/examples#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.rxresu.me/self-hosting/examples#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Server Configuration\",\"item\":\"https://docs.rxresu.me/self-hosting/environment-variables\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Deployment examples\",\"item\":\"https://docs.rxresu.me/self-hosting/examples\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://docs.rxresu.me/self-hosting/examples#article\",\"headline\":\"Deployment examples\",\"name\":\"Deployment examples\",\"description\":\"Copy-ready setups for self-hosted Reactive Resume: Caddy, Traefik and nginx with HTTPS, S3 storage, Redis, local AI with Ollama, and more.\",\"url\":\"https://docs.rxresu.me/self-hosting/examples\",\"mainEntityOfPage\":{\"@id\":\"https://docs.rxresu.me/self-hosting/examples#webpage\"},\"image\":\"https://rxresu.me/opengraph/banner.jpg\",\"dateModified\":\"2026-10-26T23:22:07.565Z\",\"publisher\":{\"@id\":\"https://docs.rxresu.me/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.rxresu.me#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Reactive Resume\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"Reactive Resume","nav":{"global":{"anchors":[{"anchor":"Reactive Resume","icon":"link","href":"https://rxresu.me"},{"anchor":"Source Code","icon":"github","href":"https://github.com/reactive-resume/reactive-resume"}]},"tabs":[{"tab":"Documentation","icon":"book","groups":[{"group":"Getting Started","pages":[{"title":"Introduction","href":"/getting-started"},{"title":"Quickstart","href":"/getting-started/quickstart"},{"title":"Video Guides","icon":"circle-play","href":"/guides/video-guides"},{"title":"What's new in v6","href":"/guides/whats-new-in-v6"},{"title":"Using the command bar","href":"/guides/using-the-command-bar"},{"title":"Keyboard shortcuts","href":"/guides/keyboard-shortcuts"},{"title":"Checking service status","href":"/guides/checking-service-status"},{"title":"Accessing the previous version","href":"/guides/accessing-the-previous-version"}]},{"group":"Account","pages":[{"title":"Creating an account","href":"/guides/creating-an-account"},{"title":"Signing in","href":"/guides/signing-in"},{"title":"Updating your profile","href":"/guides/updating-your-profile"},{"title":"Changing appearance and language","href":"/guides/changing-appearance-and-language"},{"title":"Linking social accounts","href":"/guides/linking-social-accounts"},{"title":"Setting up two-step verification","href":"/guides/setting-up-two-factor-authentication"},{"title":"Setting up passkeys","href":"/guides/setting-up-passkeys"},{"title":"Exporting your data","href":"/guides/exporting-your-data"},{"title":"Deleting your account","href":"/guides/deleting-your-account"}]},{"group":"Documents","pages":[{"title":"Creating your first resume","href":"/guides/creating-your-first-resume"},{"title":"Managing your documents","href":"/guides/managing-documents"},{"title":"Organizing documents with tags","href":"/guides/organizing-with-tags"},{"title":"Using the Trash","href":"/guides/using-the-trash"},{"title":"Importing a resume","href":"/guides/importing-resumes"}]},{"group":"Resume Editor","pages":[{"title":"Getting to know the resume editor","href":"/guides/editor-overview"},{"title":"Filling in your details","href":"/guides/filling-in-your-details"},{"title":"Formatting text","href":"/guides/formatting-text"},{"title":"Managing sections","href":"/guides/managing-sections"},{"title":"Editing entries","href":"/guides/editing-entries"},{"title":"Entering dates","href":"/guides/entering-dates"},{"title":"Undoing changes and version history","href":"/guides/undoing-changes-and-version-history"},{"title":"Using private notes","href":"/guides/using-private-notes"},{"title":"Editing on a phone or tablet","href":"/guides/editing-on-mobile"}]},{"group":"Design","pages":[{"title":"Choosing a template","href":"/guides/choosing-a-template"},{"title":"Changing how entries look","href":"/guides/changing-how-entries-look"},{"title":"Customizing typography","href":"/guides/customizing-typography"},{"title":"Choosing colors","href":"/guides/choosing-colors"},{"title":"Choosing paper size, margins and language","href":"/guides/selecting-page-format"},{"title":"Arranging the layout","href":"/guides/arranging-the-layout"},{"title":"Fitting content on a page","href":"/guides/fitting-content-on-a-page"},{"title":"Applying custom styles","href":"/applying-custom-styles"}]},{"group":"Checking Your Resume","pages":[{"title":"Checking your resume before you apply","href":"/guides/checking-your-resume"},{"title":"Checking any resume PDF with the ATS checker","href":"/guides/using-the-ats-checker"}]},{"group":"Cover Letters","pages":[{"title":"Writing a cover letter","href":"/guides/writing-a-cover-letter"}]},{"group":"Sharing and Exporting","pages":[{"title":"Sharing your resume as a web page","href":"/guides/sharing-your-resume-publicly"},{"title":"Downloading your resume","href":"/guides/exporting-your-resume"},{"title":"Exporting a resume to Markdown","href":"/guides/exporting-resume-to-markdown"}]},{"group":"AI Assistant","pages":[{"title":"Connecting an AI provider","href":"/guides/using-ai"},{"title":"Using the assistant","href":"/guides/using-the-assistant"},{"title":"Assistant tools","href":"/guides/ai-agent-tools"}]},{"group":"Career and Application Workspaces","pages":[{"title":"Your career, in one place","href":"/guides/career-overview"},{"title":"Prepare your first application","href":"/guides/first-career-application"},{"title":"Tell your story once","href":"/guides/building-career-knowledge"},{"title":"Apply when your experience is different","href":"/guides/checking-job-fit"},{"title":"Answer another application form","href":"/guides/writing-application-answers"},{"title":"Get ready for interview day","href":"/guides/preparing-for-interview-day"},{"title":"Find roles while you are busy","href":"/guides/finding-roles-automatically"},{"title":"Reply to a recruiter","href":"/guides/replying-to-recruiters"},{"title":"Choose between two offers","href":"/guides/comparing-job-offers"},{"title":"Career and workspace controls","href":"/guides/career-workspace-reference"}]},{"group":"Application Tracker","pages":[{"title":"Tracking job applications","href":"/guides/tracking-job-applications"},{"title":"Adding an application","href":"/guides/adding-an-application"},{"title":"Managing an application","href":"/guides/managing-an-application"},{"title":"Customizing application stages","href":"/guides/customizing-application-stages"},{"title":"Scheduling interviews","href":"/guides/scheduling-interviews"},{"title":"Tailoring a resume for a job","href":"/guides/tailoring-a-resume-for-a-job"},{"title":"Viewing application insights","href":"/guides/viewing-application-insights"},{"title":"Importing applications from CSV","href":"/guides/importing-applications-from-csv"}]},{"group":"Integrations","pages":[{"title":"Using the API","href":"/guides/using-the-api"},{"title":"Using the patch API","href":"/guides/using-the-patch-api"},{"title":"JSON resume schema","href":"/guides/json-resume-schema"},{"title":"Using the MCP server","href":"/guides/using-the-mcp-server"},{"title":"Using WebMCP","href":"/guides/using-webmcp"},{"title":"Managing applications with MCP","href":"/guides/managing-applications-with-mcp"},{"title":"Using agent skills","href":"/guides/using-agent-skills"},{"title":"Large RPC requests","href":"/guides/large-rpc-requests"}]},{"group":"Use Cases","pages":[{"title":"Free resume builder","href":"/use-cases/free-resume-builder"},{"title":"Open-source resume builder","href":"/use-cases/open-source-resume-builder"},{"title":"Privacy-focused resume builder","href":"/use-cases/privacy-focused-resume-builder"},{"title":"Self-hosted resume builder","href":"/use-cases/self-hosted-resume-builder"},{"title":"API and MCP resume automation","href":"/use-cases/api-mcp-resume-automation"},{"title":"Export and share resumes","href":"/use-cases/export-and-share-resumes"},{"title":"AI resume builder","href":"/use-cases/ai-resume-builder"}]},{"group":"Comparisons","pages":[{"title":"Reactive Resume vs Canva","href":"/comparisons/reactive-resume-vs-canva"},{"title":"Reactive Resume vs Adobe Express","href":"/comparisons/reactive-resume-vs-adobe-express"},{"title":"Reactive Resume vs Overleaf","href":"/comparisons/reactive-resume-vs-overleaf"},{"title":"Reactive Resume vs Resume.com","href":"/comparisons/reactive-resume-vs-resume-com"},{"title":"Reactive Resume vs MyPerfectResume","href":"/comparisons/reactive-resume-vs-myperfectresume"},{"title":"Reactive Resume vs Resume-Now","href":"/comparisons/reactive-resume-vs-resume-now"},{"title":"Reactive Resume vs Resume.io","href":"/comparisons/reactive-resume-vs-resume-io"},{"title":"Reactive Resume vs Zety","href":"/comparisons/reactive-resume-vs-zety"},{"title":"Reactive Resume vs Kickresume","href":"/comparisons/reactive-resume-vs-kickresume"},{"title":"Reactive Resume vs ResumeGemini","href":"/comparisons/reactive-resume-vs-resumegemini"},{"title":"Reactive Resume vs Jobscan","href":"/comparisons/reactive-resume-vs-jobscan"},{"title":"Reactive Resume vs Resumod","href":"/comparisons/reactive-resume-vs-resumod"},{"title":"Reactive Resume vs Rezi","href":"/comparisons/reactive-resume-vs-rezi"},{"title":"Reactive Resume vs CareerCircle","href":"/comparisons/reactive-resume-vs-careercircle"},{"title":"Reactive Resume vs Novorésumé","href":"/comparisons/reactive-resume-vs-novoresume"},{"title":"Reactive Resume vs LiveCareer","href":"/comparisons/reactive-resume-vs-livecareer"},{"title":"Reactive Resume vs Freesumes","href":"/comparisons/reactive-resume-vs-freesumes"}]},{"group":"Self-Hosting","pages":[{"title":"Self-hosting with Docker","href":"/self-hosting/docker"},{"title":"Self-hosting with Kubernetes","href":"/self-hosting/kubernetes"},{"title":"Self-hosting on Vercel","href":"/self-hosting/vercel"},{"title":"Self-hosting on Hostinger","href":"/self-hosting/hostinger"},{"title":"Self-hosting on Cloudflare","href":"/self-hosting/cloudflare"},{"title":"Self-hosting on your NAS","href":"/self-hosting/nas"}]},{"group":"Server Configuration","pages":[{"title":"Environment variables","href":"/self-hosting/environment-variables"},{"title":"Sharing domains","href":"/self-hosting/sharing-domains"},{"title":"Mirror Computer Modern fonts","description":"Serve Computer Modern font files from your own host for server-generated PDFs, and understand which font requests remain external.","href":"/self-hosting/fonts"},{"title":"Deployment examples","href":"/self-hosting/examples"},{"title":"Job search and AI","description":"Configure one optional Firecrawl, Tavily or Exa connection and an AI provider without blocking manual job saving.","href":"/self-hosting/job-search-and-ai"},{"title":"Single sign-on (SSO)","href":"/self-hosting/sso"}]},{"group":"Upgrade Guides","pages":[{"title":"Migrating from v5","href":"/self-hosting/upgrading-to-v6"},{"title":"Migrating from v4","href":"/self-hosting/migration"}]},{"group":"Contributing","pages":[{"title":"Development setup","href":"/contributing/development"},{"title":"Project architecture","href":"/contributing/architecture"},{"title":"Web access adapters","href":"/contributing/web-access"},{"title":"Career platform rollout","href":"/contributing/career-platform"},{"title":"Contributing translations","href":"/contributing/translations"},{"title":"Deployment checks","href":"/contributing/deployment-checks"},{"title":"Storage cleanup jobs","href":"/contributing/storage-cleanup"}]},{"group":"Community","pages":[{"title":"Sponsors","href":"/community/sponsors"},{"title":"Spotlight","href":"/community/spotlight"}]},{"group":"Legal","pages":[{"title":"License","href":"/legal/license"},{"title":"Privacy Policy","href":"/legal/privacy-policy"},{"title":"Terms of Service","href":"/legal/terms-of-service"}]}]},{"tab":"Changelog","icon":"clock","pages":[{"title":"Changelog","href":"/changelog"}]},{"tab":"API Reference","icon":"code","pages":[{"title":"API Reference","href":"/api-reference/system/get-application-health-and-version"}]}]},"basePath":"","title":"Deployment examples - Reactive Resume","buildId":"dpl_G9nrg4qeJSZtah1Ziwbc2n6WkLch","deploymentId":"dpl_G9nrg4qeJSZtah1Ziwbc2n6WkLch","dynamics":["07c022d89b","a74be98ba7"],"chunks":["Frame-Bpq2Z6jK.js","DynamicMarkdown-D5M2At0B.js","zoom-image-lightbox-DL4wrF0w.js","lightbox-handle-r17bVUz3.js"]}