[{"data":1,"prerenderedAt":513},["ShallowReactive",2],{"guide-\u002Fself-hosting":3,"i-lucide:arrow-up-right":504,"i-lucide:arrow-right":509,"i-lucide:copy":511},{"id":4,"title":5,"body":6,"breadcrumb":473,"description":474,"extension":475,"faq":476,"heading":495,"lead":496,"meta":497,"navigation":134,"path":498,"reviewedAt":499,"seo":500,"shortAnswer":501,"stem":502,"__hash__":503},"guides\u002Fself-hosting.md","Self-host an open-source email builder — static build or Docker",{"type":7,"value":8,"toc":465},"minimark",[9,14,31,35,39,47,89,96,160,192,207,216,227,231,234,297,317,320,383,387,398,422,426,429,433,437,446,461],[10,11,13],"h2",{"id":12},"what-you-need","What you need",[15,16,17,25],"ul",{},[18,19,20,24],"li",{},[21,22,23],"strong",{},"Static build:"," a current LTS release of Node.js and pnpm — only at build time.",[18,26,27,30],{},[21,28,29],{},"Docker:"," just Docker. The image builds Card and serves it with nginx.",[32,33,34],"p",{},"Nothing else. There is no database to provision and no environment variables to configure — the app runs entirely in the visitor's browser.",[10,36,38],{"id":37},"option-1-static-build","Option 1 — static build",[32,40,41,42,46],{},"Clone the repository, install dependencies and build. The result is a ",[43,44,45],"code",{},"dist\u002F"," folder of plain static assets:",[48,49,55],"pre",{"className":50,"code":51,"filename":52,"language":53,"meta":54,"style":54},"language-sh shiki shiki-themes github-light github-dark","git clone https:\u002F\u002Fgithub.com\u002Fmysigmail\u002Fcard.git\ncd card\npnpm install\npnpm build\n# → static files in dist\u002F\n","shell","sh","",[43,56,57,65,71,77,83],{"__ignoreMap":54},[58,59,62],"span",{"class":60,"line":61},"line",1,[58,63,64],{},"git clone https:\u002F\u002Fgithub.com\u002Fmysigmail\u002Fcard.git\n",[58,66,68],{"class":60,"line":67},2,[58,69,70],{},"cd card\n",[58,72,74],{"class":60,"line":73},3,[58,75,76],{},"pnpm install\n",[58,78,80],{"class":60,"line":79},4,[58,81,82],{},"pnpm build\n",[58,84,86],{"class":60,"line":85},5,[58,87,88],{},"# → static files in dist\u002F\n",[32,90,91,92,95],{},"Point any web server at that folder. With nginx, a fallback to ",[43,93,94],{},"index.html"," keeps client-side routing working:",[48,97,102],{"className":98,"code":99,"filename":100,"language":101,"meta":54,"style":54},"language-nginx shiki shiki-themes github-light github-dark","server {\n  listen 80;\n  server_name builder.example.com;\n  root \u002Fvar\u002Fwww\u002Fcard\u002Fdist;\n  index index.html;\n\n  location \u002F {\n    try_files $uri $uri\u002F \u002Findex.html;\n  }\n}\n","nginx.conf","nginx",[43,103,104,109,114,119,124,129,136,142,148,154],{"__ignoreMap":54},[58,105,106],{"class":60,"line":61},[58,107,108],{},"server {\n",[58,110,111],{"class":60,"line":67},[58,112,113],{},"  listen 80;\n",[58,115,116],{"class":60,"line":73},[58,117,118],{},"  server_name builder.example.com;\n",[58,120,121],{"class":60,"line":79},[58,122,123],{},"  root \u002Fvar\u002Fwww\u002Fcard\u002Fdist;\n",[58,125,126],{"class":60,"line":85},[58,127,128],{},"  index index.html;\n",[58,130,132],{"class":60,"line":131},6,[58,133,135],{"emptyLinePlaceholder":134},true,"\n",[58,137,139],{"class":60,"line":138},7,[58,140,141],{},"  location \u002F {\n",[58,143,145],{"class":60,"line":144},8,[58,146,147],{},"    try_files $uri $uri\u002F \u002Findex.html;\n",[58,149,151],{"class":60,"line":150},9,[58,152,153],{},"  }\n",[58,155,157],{"class":60,"line":156},10,[58,158,159],{},"}\n",[48,161,166],{"className":162,"code":163,"filename":164,"language":165,"meta":54,"style":54},"language-txt shiki shiki-themes github-light github-dark","builder.example.com {\n  root * \u002Fvar\u002Fwww\u002Fcard\u002Fdist\n  file_server\n  try_files {path} \u002Findex.html\n}\n","Caddyfile","txt",[43,167,168,173,178,183,188],{"__ignoreMap":54},[58,169,170],{"class":60,"line":61},[58,171,172],{},"builder.example.com {\n",[58,174,175],{"class":60,"line":67},[58,176,177],{},"  root * \u002Fvar\u002Fwww\u002Fcard\u002Fdist\n",[58,179,180],{"class":60,"line":73},[58,181,182],{},"  file_server\n",[58,184,185],{"class":60,"line":79},[58,186,187],{},"  try_files {path} \u002Findex.html\n",[58,189,190],{"class":60,"line":85},[58,191,159],{},[32,193,194,195,198,199,202,203,206],{},"The same build deploys to any static host — Cloudflare Pages, Netlify or GitHub Pages. Set the build command to ",[43,196,197],{},"pnpm build"," and the output directory to ",[43,200,201],{},"dist",". For a GitHub Pages project site such as ",[43,204,205],{},"https:\u002F\u002Fuser.github.io\u002Fcard\u002F",", build with the repository path so asset URLs resolve correctly:",[48,208,210],{"className":50,"code":209,"filename":52,"language":53,"meta":54,"style":54},"VITE_APP_BASE_PATH=\u002Fcard\u002F pnpm build\n",[43,211,212],{"__ignoreMap":54},[58,213,214],{"class":60,"line":61},[58,215,209],{},[32,217,218,219,222,223,226],{},"Replace ",[43,220,221],{},"\u002Fcard\u002F"," with your repository name. A custom domain served from its root can keep the default ",[43,224,225],{},"\u002F"," base path.",[10,228,230],{"id":229},"option-2-docker","Option 2 — Docker",[32,232,233],{},"There is no official image yet, so the container is built from source. A two-stage Dockerfile keeps the final image small: Node builds the static files, nginx serves them.",[48,235,240],{"className":236,"code":237,"filename":238,"language":239,"meta":54,"style":54},"language-dockerfile shiki shiki-themes github-light github-dark","FROM node:22-alpine AS build\nWORKDIR \u002Fapp\nRUN npm install -g pnpm\nCOPY package.json pnpm-lock.yaml .\u002F\nRUN pnpm install --frozen-lockfile\nCOPY . .\nRUN pnpm build\n\nFROM nginx:alpine\nCOPY --from=build \u002Fapp\u002Fdist \u002Fusr\u002Fshare\u002Fnginx\u002Fhtml\nEXPOSE 80\n","Dockerfile","dockerfile",[43,241,242,247,252,257,262,267,272,277,281,286,291],{"__ignoreMap":54},[58,243,244],{"class":60,"line":61},[58,245,246],{},"FROM node:22-alpine AS build\n",[58,248,249],{"class":60,"line":67},[58,250,251],{},"WORKDIR \u002Fapp\n",[58,253,254],{"class":60,"line":73},[58,255,256],{},"RUN npm install -g pnpm\n",[58,258,259],{"class":60,"line":79},[58,260,261],{},"COPY package.json pnpm-lock.yaml .\u002F\n",[58,263,264],{"class":60,"line":85},[58,265,266],{},"RUN pnpm install --frozen-lockfile\n",[58,268,269],{"class":60,"line":131},[58,270,271],{},"COPY . .\n",[58,273,274],{"class":60,"line":138},[58,275,276],{},"RUN pnpm build\n",[58,278,279],{"class":60,"line":144},[58,280,135],{"emptyLinePlaceholder":134},[58,282,283],{"class":60,"line":150},[58,284,285],{},"FROM nginx:alpine\n",[58,287,288],{"class":60,"line":156},[58,289,290],{},"COPY --from=build \u002Fapp\u002Fdist \u002Fusr\u002Fshare\u002Fnginx\u002Fhtml\n",[58,292,294],{"class":60,"line":293},11,[58,295,296],{},"EXPOSE 80\n",[48,298,300],{"className":50,"code":299,"filename":52,"language":53,"meta":54,"style":54},"docker build -t mysigmail-card .\ndocker run -d --name card -p 8080:80 mysigmail-card\n# → http:\u002F\u002Flocalhost:8080\n",[43,301,302,307,312],{"__ignoreMap":54},[58,303,304],{"class":60,"line":61},[58,305,306],{},"docker build -t mysigmail-card .\n",[58,308,309],{"class":60,"line":67},[58,310,311],{},"docker run -d --name card -p 8080:80 mysigmail-card\n",[58,313,314],{"class":60,"line":73},[58,315,316],{},"# → http:\u002F\u002Flocalhost:8080\n",[32,318,319],{},"Or with Docker Compose, if Card joins an existing stack:",[48,321,326],{"className":322,"code":323,"filename":324,"language":325,"meta":54,"style":54},"language-yaml shiki shiki-themes github-light github-dark","services:\n  card:\n    build: .\n    ports:\n      - \"8080:80\"\n    restart: unless-stopped\n","compose.yaml","yaml",[43,327,328,338,345,357,364,373],{"__ignoreMap":54},[58,329,330,334],{"class":60,"line":61},[58,331,333],{"class":332},"s9eBZ","services",[58,335,337],{"class":336},"sVt8B",":\n",[58,339,340,343],{"class":60,"line":67},[58,341,342],{"class":332},"  card",[58,344,337],{"class":336},[58,346,347,350,353],{"class":60,"line":73},[58,348,349],{"class":332},"    build",[58,351,352],{"class":336},": ",[58,354,356],{"class":355},"sj4cs",".\n",[58,358,359,362],{"class":60,"line":79},[58,360,361],{"class":332},"    ports",[58,363,337],{"class":336},[58,365,366,369],{"class":60,"line":85},[58,367,368],{"class":336},"      - ",[58,370,372],{"class":371},"sZZnC","\"8080:80\"\n",[58,374,375,378,380],{"class":60,"line":131},[58,376,377],{"class":332},"    restart",[58,379,352],{"class":336},[58,381,382],{"class":371},"unless-stopped\n",[10,384,386],{"id":385},"updating","Updating",[32,388,389,390,397],{},"Updates are a pull and a rebuild — there is no migration step, because there is no server-side state. Versions and changelogs are published on the ",[391,392,396],"a",{"href":393,"rel":394},"https:\u002F\u002Fgithub.com\u002Fmysigmail\u002Fcard\u002Freleases",[395],"nofollow","GitHub releases page",".",[48,399,401],{"className":50,"code":400,"filename":52,"language":53,"meta":54,"style":54},"cd card\ngit pull\npnpm install && pnpm build\n# Docker: docker compose up -d --build\n",[43,402,403,407,412,417],{"__ignoreMap":54},[58,404,405],{"class":60,"line":61},[58,406,70],{},[58,408,409],{"class":60,"line":67},[58,410,411],{},"git pull\n",[58,413,414],{"class":60,"line":73},[58,415,416],{},"pnpm install && pnpm build\n",[58,418,419],{"class":60,"line":79},[58,420,421],{},"# Docker: docker compose up -d --build\n",[10,423,425],{"id":424},"what-youre-actually-running","What you're actually running",[32,427,428],{},"A self-hosted Card deployment is deliberately boring to operate. Everything a template touches stays in the browser of the person editing it:",[430,431],"guide-specs",{":items":432},"[{\"label\":\"Backend\",\"value\":\"None — static files only\"},{\"label\":\"Database\",\"value\":\"None\"},{\"label\":\"Template storage\",\"value\":\"Browser localStorage\"},{\"label\":\"HTML \u002F JSON export\",\"value\":\"Generated in the browser\"},{\"label\":\"Telemetry\",\"value\":\"Off by default\"},{\"label\":\"Licence\",\"value\":\"AGPL-3.0\"}]",[10,434,436],{"id":435},"licensing-for-self-hosters","Licensing for self-hosters",[32,438,439,440,445],{},"Card is distributed under ",[391,441,444],{"href":442,"rel":443},"https:\u002F\u002Fgithub.com\u002Fmysigmail\u002Fcard\u002Fblob\u002Fmain\u002FLICENSE",[395],"AGPL-3.0",". In practice, for a self-hosted deployment:",[15,447,448,451,454],{},[18,449,450],{},"Running Card for yourself or inside your company is free, including commercial use.",[18,452,453],{},"If you modify Card and serve it to users over a network, those users must be able to get the source of your modified version.",[18,455,456,457,397],{},"Embedding the editor in a closed-source product requires a ",[391,458,460],{"href":459},"mailto:reshetov.art@gmail.com?subject=Card%20commercial%20licence","commercial licence",[462,463,464],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":54,"searchDepth":67,"depth":67,"links":466},[467,468,469,470,471,472],{"id":12,"depth":67,"text":13},{"id":37,"depth":67,"text":38},{"id":229,"depth":67,"text":230},{"id":385,"depth":67,"text":386},{"id":424,"depth":67,"text":425},{"id":435,"depth":67,"text":436},"Self-hosting","Step-by-step guide to self-hosting MySigMail Card, an open-source drag-and-drop HTML email template builder: a static build behind nginx or Caddy, or a Docker container. No backend, no database, AGPL-3.0.","md",[477,480,483,486,489,492],{"question":478,"answer":479},"Is there an official Docker image?","Not yet. The recommended way to run Card in Docker is the multi-stage Dockerfile from this guide: a Node stage builds the static files, an nginx stage serves them. Watch the GitHub releases page for an official image.",{"question":481,"answer":482},"Does Card need a database or a backend?","No. Card is a client-side Vue application. A deployment is a folder of static files behind any web server. Templates auto-save to the browser's localStorage, and HTML\u002FJSON exports are generated in the browser.",{"question":484,"answer":485},"Can my company self-host Card for free?","Yes. Card is licensed under AGPL-3.0, which permits free commercial use, including internal company deployments. If you modify Card and make it available to users over a network, the licence requires you to offer them the source of your modified version. To embed the editor in a closed-source product instead, a commercial licence is available.",{"question":487,"answer":488},"How do I update a self-hosted instance?","Pull the latest code and rebuild: git pull, pnpm install, pnpm build — or rebuild the Docker image. Releases are published on GitHub with changelogs.",{"question":490,"answer":491},"What are the server requirements?","Any server that can serve static files: nginx, Caddy, Apache, or a static host like Cloudflare Pages or Netlify. The build output is a few megabytes of assets with no server-side runtime. Node.js is only needed at build time.",{"question":493,"answer":494},"Can I add my own blocks or templates?","Yes. Card is a Vue 3 + TypeScript codebase, and the block catalog can be extended in code. Keep in mind that AGPL-3.0 applies to modified versions you serve to users over a network.","Self-host the email template builder.","Card is a client-side Vue app: no backend, no database, no accounts. This guide covers both ways to run it on your own infrastructure — a plain static build behind nginx or Caddy, and a Docker container — plus what AGPL-3.0 means for company use.",{},"\u002Fself-hosting","2026-08-03",{"title":5,"description":474},"Build once, serve static files. Five minutes with a web server you already run.","self-hosting","v8mYZdgDW5aGLxcp5S4OPQxOlYs-urZZucGOCbWGV_Q",{"left":505,"top":505,"width":506,"height":506,"rotate":505,"vFlip":507,"hFlip":507,"body":508},0,24,false,"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M7 7h10v10M7 17L17 7\"\u002F>",{"left":505,"top":505,"width":506,"height":506,"rotate":505,"vFlip":507,"hFlip":507,"body":510},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 12h14m-7-7l7 7l-7 7\"\u002F>",{"left":505,"top":505,"width":506,"height":506,"rotate":505,"vFlip":507,"hFlip":507,"body":512},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Crect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"\u002F>\u003Cpath d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"\u002F>\u003C\u002Fg>",1786067482617]