Run Postern under Docker on a machine you own, then open its Console.
Before you start
Docker.Install Docker first. It is the only thing
you install by hand.
A machine that stays on. While it is off, your agents reach nothing. Keep it updated, the
same as anything else you run.
About 10 minutes, plus the time the image takes to download.
Somewhere to keep one password. You create it in step 2. Postern’s database
never accepts a different one afterwards, so open your password manager now.
Windows: not tested. Postern runs under Docker, so Windows should work. Nobody has run it
there. Treat every Windows line on this page as untested.
another computer — a NAS, a spare machine, a computer with no screen
Sign in to that computer over SSH. Run steps 1 to 3 there, then forward the Console port in step 4.
Postern runs either way. A NAS or a spare machine works the same as the computer in front of you.
The second path needs SSH on that computer, and nothing else changes.
1
Get the compose file
Open a terminal on the machine that will run Postern. On macOS, press Command-Space, type
Terminal, press Return. On Windows, press the Windows key, type PowerShell, press
Return.Make a folder and download the one file Postern needs:
mkdir postern && cd posterncurl -O https://raw.githubusercontent.com/getpostern/postern/main/docker-compose.yml
On Windows, type curl.exe — in PowerShell, curl means something else. Nobody has tested these
lines:
mkdir postern; cd posterncurl.exe -O https://raw.githubusercontent.com/getpostern/postern/main/docker-compose.yml
The folder now holds one file, docker-compose.yml. Every command below runs from this folder.You can clone the whole repository instead, if you have Git:
git clone https://github.com/getpostern/postern.git && cd postern. Postern needs nothing else
from it.
2
Write the database password
Postern’s database needs one password. It lives in a file named .env, beside
docker-compose.yml.
Postgres records this password the first time it starts. It never accepts a different one against
the same data, and there is no way to change it afterwards. Keep .env. Do not type the password
in front of docker compose up. That creates a new one on every run, and every other
docker compose command then fails.
In that folder, run the line for your system. It makes a password of letters and digits only. A
/, :, @, ?, #, & or + breaks the address Postern uses to reach its database. Postern
then exits at once. openssl rand -base64 32 is not a substitute: its output holds those characters
often.
.env now holds one line: POSTGRES_PASSWORD= and 48 letters and digits. That is the whole of
Postern’s required configuration. Every other setting has a working default.Copy that line into your password manager now. The leading dot makes .env invisible in Finder and
File Explorer. Do not make .env in Notepad — Notepad adds .txt to the name.
3
Start Postern
Docker must be open first. On macOS and Windows, open the Docker Desktop application and wait
for it to finish its own start-up. On Linux, Docker runs as a service and needs nothing opened.From the folder that holds docker-compose.yml, start Postern:
docker compose up -d
The first run downloads the image ghcr.io/getpostern/postern, so it takes minutes. Later runs take
seconds. The image runs on Intel and AMD machines, and on ARM machines such as Apple Silicon.
Postern sets up its database tables before it answers anything, so port 8787 stays quiet for a few
seconds after the command returns.Postern opens two ports to this computer only: 8787, the Console port, and 8788, the agent
port. Only this computer can reach these addresses. Nothing on your Wi-Fi, and nothing on the
internet, can. The database gets no port at all. Only Postern reaches it, over the private network
Docker Compose creates.
If another program already holds 8787 or 8788, the app container does not start. Add a line to
.env, below the password line:
REST_PORT=8790
Then run docker compose up -d again. The Console now answers at http://localhost:8790, and
every 8787 below becomes 8790. MCP_HTTP_PORT moves the agent port the same way. If you
already connected Google, Microsoft or
WHOOP, change the address you registered there to the new port. They match it
exactly, character for character.
Docker keeps two storage areas outside the containers: your data, and the key that unlocks every
account you connect. docker compose down -v deletes both, permanently. Every account you
connected becomes unreadable, and nothing can recover them. docker compose down without -v is
safe: it removes the containers and leaves both storage areas alone.Take a copy of the key: Back up, export and erase. If Postern cannot
open the vault at start-up, it refuses to start rather than starting broken. A database restored
without its key stops there.
The VAULT KEY IS NOT BACKED UP notice. Postern prints it in its own log at every start. It
is a reminder, not an error, and Postern runs anyway. It appears while Postern holds the key
itself and no marker file sits beside it.The notice tells you to acknowledge the copy in the Console. No such control ships. Nothing in
the Console writes that marker, and no address answers it. So the notice still appears after you
hold a good copy.Two things stop it. Take your copy of the key first, then choose one.
Write the marker yourself. Postern looks for a file named master.key.backed-up beside the
key. From the folder that holds docker-compose.yml:
Postern checks that the file is there. It never reads what is in it. Write the date anyway. It
tells a later you when you took the copy.
Hold the key yourself. Put PCI_MASTER_KEY in .env — a base64 32-byte key you keep —
and restart. Make one with openssl rand -base64 32. Set it before the first start. A new key
against accounts encrypted under an old one makes Postern refuse to start.
A restore brings the notice back. The marker sat in the old storage area, and the restore makes a
new one.
Four commands cover the first week. Run them from the same folder:
docker compose ps # is it updocker compose logs -f app # read its logdocker compose stop # pause it; docker compose start resumesdocker compose pull && docker compose up -d # move to a newer image
To hold one release instead of the newest, add POSTERN_VERSION=v0.1.0 to .env.
4
Open the Console
The Console answers at http://localhost:8787, and only on the machine Postern runs on.If Postern runs on the computer in front of you, open
http://localhost:8787.If Postern runs on another computer, forward the Console port to your own computer. Run this on
your own computer, not on the machine Postern runs on. Turn on SSH on that machine first.
Replace YOUR_MACHINE with however you normally reach that computer: a name, a user@host, or an
IP address. The terminal then prints nothing and does not return to a prompt. That is the command at
work, not a hang. Leave that window open, use a second terminal window for anything else, and press
Control-C when you want to stop. Now open http://localhost:8787 on your
own computer.Anyone who can reach the Console port can change your settings and create agent keys. It has no
password, so it stays on this computer. Do not change the address in docker-compose.yml to your
network. Do not put a tunnel service in front of it. The full argument is in
The Console has no authentication.The Console opens on a page headed Your gate is new. with one button, Add your first source.
The left sidebar lists Gatehouse, then the six sectors, then Sources, Agents & keys,
The ledger and Settings. A sector is one area of your life: finance, mail, calendar,
contacts, health, home. It is the smallest thing you can grant.
The Console on a fresh install. The one button reads Add your first source.
To let an agent reach Postern from another device, see Remote access. It is
off by default, and it never moves the Console off this machine.
docker compose up -d stops at once and prints set POSTGRES_PASSWORD in .env — generate one with: openssl rand -hex 24
.env is missing or empty. Run the step 2 command again, in the folder that holds docker-compose.yml.
app starts and exits within seconds, and your password holds a character that is not a letter or a digit
Run the step 2 command again, then docker compose down -v && docker compose up -d. That wipes the database, which is safe only before you connect anything.
db is up, app cannot sign in to the database, and .env changed since the first start
Postgres kept the first password. Put the original line back in .env, then run docker compose up -d.
app does not start, and another program on this computer already uses 8787 or 8788
Move Postern’s port. Step 3 carries the line to add.
The browser shows a not-found page while curl -s localhost:8787/healthz still answers {"status":"ok"}
Postern runs, and its Console files are not where it looked. Run docker compose logs app; the log names the folder it tried. Set PCI_CONSOLE_DIR in .env to the right folder.
The log prints VAULT KEY IS NOT BACKED UP at every start
That is the reminder, not a fault. Step 3 has the two ways to stop it.
Postern runs on a machine you own. Docker downloaded a finished image from
ghcr.io/getpostern/postern and built nothing on that machine. There is no Postern account and no
Postern server in the middle. You have connected nothing yet, and no agent can reach anything until
you grant it.Three things are now yours to keep: the machine, on and updated; the one line in .env; and a copy
of the vault key.
a name, the sectors it opens, a deadline · a few minutes · Postern shows the key once
Set up remote access
Tailscale, and Funnel only if a hosted agent needs a public address · about 20 minutes, once ·
off by default
{"docsConfig":{"theme":"sequoia","$schema":"https://mintlify.com/docs.json","name":"Postern","colors":{"primary":"#E7A42A","light":"#E7A42A","dark":"#B83E15"},"favicon":"https://mintcdn.com/postern/L5OSSMQX3JjXD-Ob/favicon.svg?fit=max\u0026auto=format\u0026n=L5OSSMQX3JjXD-Ob\u0026q=85\u0026s=acbbe5732970fc6bf76b003f012843f1","appearance":{"default":"light","strict":false},"background":{"color":{"light":"#F5EDDA","dark":"#151F19"}},"navbar":{"links":[{"href":"https://getpostern.com","label":"getpostern.com"}],"primary":{"type":"github","href":"https://github.com/getpostern/postern"}},"footer":{"socials":{"website":"https://getpostern.com","github":"https://github.com/getpostern/postern"}},"fonts":{"heading":{"family":"Cormorant Garamond","source":"fonts/cormorant-garamond-var.woff2","format":"woff2"},"body":{"family":"Source Serif 4","source":"fonts/source-serif-4-var.woff2","format":"woff2"}},"styling":{"codeblocks":"system","latex":false},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"postern","hasAudioNarration":false,"actualSubdomain":"postern","customDomains":["docs.getpostern.com"],"gitSource":{"type":"github","owner":"getpostern","repo":"docs","deployBranch":"main","contentDirectory":"","isPrivate":false,"mountPath":"","label":""},"chroma":{"latestBuiltDeploymentHistoryId":"6a77bc437948e45f24bff423","collectionId":"465e71f9-edfd-4e92-a7b2-36446667a983","liveDeploymentHistoryId":"6a77bc437948e45f24bff423","schemaVersion":"v3"},"entitlements":{"AI_CHAT":{"status":"DISABLED"}},"buildId":"6aea219dcf56d9cceaaf5561:success","clientVersion":"0.0.4076","searchFilterCounts":[{"language":"default","tag":"Reference","count":15},{"language":"default","tag":"Connect a source","count":9},{"language":"default","tag":"Start here","count":6},{"language":"default","tag":"MCP tools","count":5},{"language":"default","tag":"Security","count":4},{"language":"default","tag":"Mail, calendar \u0026 contacts","count":3},{"language":"default","tag":"Agents, keys and grants","count":3},{"language":"default","tag":"The record","count":3},{"language":"default","tag":"Health","count":2},{"language":"default","tag":"Money","count":2}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true},"basePath":"","hasChatPermissions":false,"hasWebMcpPermissions":false,"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":"rbac-expansion","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":"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":"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":"editor-metadata","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},{"name":"turbopuffer-serve","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"live-agents","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"comment-mode","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":false},"customCss":[{"filePath":"style.css","content":"@font-face{font-family:\"Courier Prime\";src:url(\"/fonts/courier-prime-400.woff2\") format(\"woff2\");font-weight:400;font-style:normal;font-display:swap}@font-face{font-family:\"Courier Prime\";src:url(\"/fonts/courier-prime-700.woff2\") format(\"woff2\");font-weight:700;font-style:normal;font-display:swap}@font-face{font-family:\"Cormorant Garamond\";src:url(\"/fonts/cormorant-garamond-marks.woff2\") format(\"woff2\");font-weight:300 700;font-style:normal;font-display:swap;unicode-range:U+2116, U+2190-2192}@font-face{font-family:\"Cormorant Garamond\";src:url(\"/fonts/cormorant-garamond-italic-marks.woff2\") format(\"woff2\");font-weight:300 700;font-style:italic;font-display:swap;unicode-range:U+2116, U+2190-2192}@font-face{font-family:\"EB Garamond\";src:url(\"/fonts/eb-garamond-marks.woff2\") format(\"woff2\");font-weight:400 800;font-style:normal;font-display:swap;unicode-range:U+2116, U+2190-2192}@font-face{font-family:\"EB Garamond\";src:url(\"/fonts/eb-garamond-italic-marks.woff2\") format(\"woff2\");font-weight:400 800;font-style:italic;font-display:swap;unicode-range:U+2116, U+2190-2192}@font-face{font-family:\"Cormorant Garamond\";src:url(\"/fonts/cormorant-garamond-500-italic.woff2\") format(\"woff2\");font-weight:500;font-style:italic;font-display:swap}@font-face{font-family:\"EB Garamond\";src:url(\"/fonts/eb-garamond-400-italic.woff2\") format(\"woff2\");font-weight:400;font-style:italic;font-display:swap}code,pre,kbd,samp,code span,pre span{font-family:\"Courier Prime\", \"Courier New\", ui-monospace, Menlo, Consolas,\n \"DejaVu Sans Mono\", monospace}@font-face{font-family:\"Source Serif 4\";src:url(\"/fonts/source-serif-4-var.woff2\") format(\"woff2\");font-weight:400 700;font-style:normal;font-display:swap}@font-face{font-family:\"Source Serif 4\";src:url(\"/fonts/source-serif-4-italic-var.woff2\") format(\"woff2\");font-weight:400 700;font-style:italic;font-display:swap}:root{--postern-body: \"Source Serif 4\", \"EB Garamond\", Georgia, serif;--postern-measure: 74ch}#content-area p,#content-area li,#content-area td,#content-area th,#content-area blockquote{font-family:var(--postern-body);font-size:17px;line-height:1.7}#content-area p,#content-area ul,#content-area ol,#content-area blockquote{max-width:var(--postern-measure)}#content-area h2{margin-top:2.75em;margin-bottom:0.6em;letter-spacing:-0.01em}#content-area h3{margin-top:2em;margin-bottom:0.5em}#content-area h2+p,#content-area h3+p{margin-top:0}#content-area table{font-size:15.5px;line-height:1.55}#content-area th{font-size:13px;letter-spacing:0.06em;text-transform:uppercase;opacity:0.72}#content-area td,#content-area th{padding-top:0.6em;padding-bottom:0.6em;vertical-align:baseline}#content-area .prose>table,#content-area table{display:block;overflow-x:auto;max-width:100%}#content-area .callout,#content-area [class*=\"callout\"]{border-width:1px;border-radius:3px}#content-area .callout p,#content-area [class*=\"callout\"] p{font-size:16px;line-height:1.65;max-width:none}#content-area p code,#content-area li code,#content-area td code{font-size:0.94em;padding:0.1em 0.32em;border-radius:2px}#content-area p strong,#content-area li strong,#content-area td strong{font-weight:600;color:var(--postern-strong, inherit)}#content-area h2:has(+p){text-wrap:balance}@media (max-width: 900px){#content-area p,#content-area li,#content-area td,#content-area th,#content-area blockquote{font-size:16px}#content-area h2{margin-top:2.25em}}"}],"page":{"pagePath":"start/install","slug":"/start/install","pageMetadata":{"title":"Install Postern","description":"Run Postern under Docker on a machine you own, then open its Console.","href":"/start/install"},"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: \"Run Postern under Docker on a machine you own, then open its Console.\"\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`,tbody:`tbody`,td:`td`,th:`th`,thead:`thead`,tr:`tr`,ul:`ul`,..._provideComponents(),...props.components},{Card,CodeBlock,Columns,Frame,Heading,Info,Note,OptimizedImage,Step,Steps,Table,Warning}=_components;if(!Card)_missingMdxReference(`Card`,true);if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Columns)_missingMdxReference(`Columns`,true);if(!Frame)_missingMdxReference(`Frame`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Info)_missingMdxReference(`Info`,true);if(!Note)_missingMdxReference(`Note`,true);if(!OptimizedImage)_missingMdxReference(`OptimizedImage`,true);if(!Step)_missingMdxReference(`Step`,true);if(!Steps)_missingMdxReference(`Steps`,true);if(!Table)_missingMdxReference(`Table`,true);if(!Warning)_missingMdxReference(`Warning`,true);return _jsxs(_Fragment,{children:[_jsxs(Info,{children:[_jsx(_components.p,{children:_jsx(_components.strong,{children:`Before you start`})}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Docker.`}),` `,_jsx(_components.a,{href:`https://docs.docker.com/get-docker/`,children:`Install Docker`}),` first. It is the only thing\nyou install by hand.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`A machine that stays on.`}),` While it is off, your agents reach nothing. Keep it updated, the\nsame as anything else you run.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`About 10 minutes,`}),` plus the time the image takes to download.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Somewhere to keep one password.`}),` You create it in `,_jsx(_components.a,{href:`#password`,children:`step 2`}),`. Postern’s database\nnever accepts a different one afterwards, so open your password manager now.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Windows: not tested.`}),` Postern runs under Docker, so Windows should work. Nobody has run it\nthere. Treat every Windows line on this page as untested.`]}),`\n`]})]}),`\n`,_jsx(Heading,{level:`2`,id:`choose-your-path`,children:`Choose your path`}),`\n`,_jsxs(Table,{children:[_jsx(_components.thead,{children:_jsxs(_components.tr,{children:[_jsx(_components.th,{children:`Postern will run on…`}),_jsx(_components.th,{children:`Then`})]})}),_jsxs(_components.tbody,{children:[_jsxs(_components.tr,{children:[_jsx(_components.td,{children:`the computer in front of you`}),_jsx(_components.td,{children:`Follow all four steps as written.`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:`another computer — a NAS, a spare machine, a computer with no screen`}),_jsxs(_components.td,{children:[`Sign in to that computer over SSH. Run steps 1 to 3 there, then `,_jsx(_components.a,{href:`#open-console`,children:`forward the Console port`}),` in step 4.`]})]})]})]}),`\n`,_jsx(_components.p,{children:`Postern runs either way. A NAS or a spare machine works the same as the computer in front of you.\nThe second path needs SSH on that computer, and nothing else changes.`}),`\n`,_jsxs(Steps,{children:[_jsxs(Step,{title:`Get the compose file`,titleSize:`h2`,id:`get-file`,children:[_jsxs(_components.p,{children:[`Open a terminal on the machine that will run Postern. On macOS, press `,_jsx(_components.strong,{children:`Command-Space`}),`, type\n`,_jsx(_components.code,{children:`Terminal`}),`, press `,_jsx(_components.strong,{children:`Return`}),`. On Windows, press the `,_jsx(_components.strong,{children:`Windows`}),` key, type `,_jsx(_components.code,{children:`PowerShell`}),`, press\n`,_jsx(_components.strong,{children:`Return`}),`.`]}),_jsx(_components.p,{children:`Make a folder and download the one file Postern needs:`}),_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:`mkdir`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` postern`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` \u0026\u0026 `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#DCDCAA`},children:`cd`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` postern`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`curl`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -O`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` https://raw.githubusercontent.com/getpostern/postern/main/docker-compose.yml`})]}),`\n`]})})}),_jsxs(_components.p,{children:[`On Windows, type `,_jsx(_components.code,{children:`curl.exe`}),` — in PowerShell, `,_jsx(_components.code,{children:`curl`}),` means something else. Nobody has tested these\nlines:`]}),_jsx(CodeBlock,{filename:``,numberOfLines:`2`,language:`powershell`,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:`powershell`,children:_jsxs(_components.code,{language:`powershell`,numberOfLines:`2`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`mkdir postern; cd postern`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#DCDCAA`},children:`curl.exe`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` -`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`O https:`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`//`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#DCDCAA`},children:`raw.githubusercontent.com`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`/`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`getpostern`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`/`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`postern`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`/`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`main`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`/`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`docker`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`-`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`compose.yml`})]}),`\n`]})})}),_jsxs(_components.p,{children:[`The folder now holds one file, `,_jsx(_components.code,{children:`docker-compose.yml`}),`. Every command below runs from this folder.`]}),_jsxs(_components.p,{children:[`You can clone the whole repository instead, if you have Git:\n`,_jsx(_components.code,{children:`git clone https://github.com/getpostern/postern.git \u0026\u0026 cd postern`}),`. Postern needs nothing else\nfrom it.`]})]}),_jsxs(Step,{title:`Write the database password`,titleSize:`h2`,id:`password`,children:[_jsxs(_components.p,{children:[`Postern’s database needs one password. It lives in a file named `,_jsx(_components.code,{children:`.env`}),`, beside\n`,_jsx(_components.code,{children:`docker-compose.yml`}),`.`]}),_jsx(Warning,{children:_jsxs(_components.p,{children:[`Postgres records this password the first time it starts. It never accepts a different one against\nthe same data, and there is no way to change it afterwards. Keep `,_jsx(_components.code,{children:`.env`}),`. Do not type the password\nin front of `,_jsx(_components.code,{children:`docker compose up`}),`. That creates a new one on every run, and every other\n`,_jsx(_components.code,{children:`docker compose`}),` command then fails.`]})}),_jsxs(_components.p,{children:[`In that folder, run the line for your system. It makes a password of letters and digits only. A\n`,_jsx(_components.code,{children:`/`}),`, `,_jsx(_components.code,{children:`:`}),`, `,_jsx(_components.code,{children:`@`}),`, `,_jsx(_components.code,{children:`?`}),`, `,_jsx(_components.code,{children:`#`}),`, `,_jsx(_components.code,{children:`\u0026`}),` or `,_jsx(_components.code,{children:`+`}),` breaks the address Postern uses to reach its database. Postern\nthen exits at once. `,_jsx(_components.code,{children:`openssl rand -base64 32`}),` is not a substitute: its output holds those characters\noften.`]}),_jsx(CodeBlock,{filename:``,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:`#0550AE`,\"--shiki-dark\":`#DCDCAA`},children:`printf`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` 'POSTGRES_PASSWORD=%s\\\\n'`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"$(`}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`openssl`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` rand `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:`-hex`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:` 24`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`)\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` >`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` .env`})]}),`\n`]})})}),_jsx(_components.p,{children:`On Windows, run these four lines in PowerShell. Nobody has tested them:`}),_jsx(CodeBlock,{filename:``,numberOfLines:`4`,language:`powershell`,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:`powershell`,children:_jsxs(_components.code,{language:`powershell`,numberOfLines:`4`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$b`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#DCDCAA`},children:` New-Object`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` byte[] `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`24`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`[`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`Security.Cryptography.RandomNumberGenerator`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`]::Create().GetBytes(`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$b`}),_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\":`#9CDCFE`},children:`$hex`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` [`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`BitConverter`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`]::ToString(`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$b`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`).Replace(`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`'-'`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`,`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`''`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`).ToLower()`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#DCDCAA`},children:`Set-Content`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` -`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`Path .env `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`-`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`Value `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"POSTGRES_PASSWORD=`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$hex`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` -`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`Encoding ascii`})]}),`\n`]})})}),_jsxs(_components.p,{children:[_jsx(_components.code,{children:`.env`}),` now holds one line: `,_jsx(_components.code,{children:`POSTGRES_PASSWORD=`}),` and 48 letters and digits. That is the whole of\nPostern’s required configuration. Every other setting has a working default.`]}),_jsxs(_components.p,{children:[`Copy that line into your password manager now. The leading dot makes `,_jsx(_components.code,{children:`.env`}),` invisible in Finder and\nFile Explorer. Do not make `,_jsx(_components.code,{children:`.env`}),` in Notepad — Notepad adds `,_jsx(_components.code,{children:`.txt`}),` to the name.`]})]}),_jsxs(Step,{title:`Start Postern`,titleSize:`h2`,id:`start`,children:[_jsxs(_components.p,{children:[`Docker must be open first. On macOS and Windows, open the `,_jsx(_components.strong,{children:`Docker Desktop`}),` application and wait\nfor it to finish its own start-up. On Linux, Docker runs as a service and needs nothing opened.`]}),_jsxs(_components.p,{children:[`From the folder that holds `,_jsx(_components.code,{children:`docker-compose.yml`}),`, start Postern:`]}),_jsx(CodeBlock,{filename:``,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:`#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`})]}),`\n`]})})}),_jsxs(_components.p,{children:[`The first run downloads the image `,_jsx(_components.code,{children:`ghcr.io/getpostern/postern`}),`, so it takes minutes. Later runs take\nseconds. The image runs on Intel and AMD machines, and on ARM machines such as Apple Silicon.\nPostern sets up its database tables before it answers anything, so port 8787 stays quiet for a few\nseconds after the command returns.`]}),_jsxs(_components.p,{children:[`Postern opens two ports to this computer only: `,_jsx(_components.strong,{children:`8787`}),`, the Console port, and `,_jsx(_components.strong,{children:`8788`}),`, the agent\nport. Only this computer can reach these addresses. Nothing on your Wi-Fi, and nothing on the\ninternet, can. The database gets no port at all. Only Postern reaches it, over the private network\nDocker Compose creates.`]}),_jsxs(Note,{children:[_jsxs(_components.p,{children:[`If another program already holds 8787 or 8788, the `,_jsx(_components.code,{children:`app`}),` container does not start. Add a line to\n`,_jsx(_components.code,{children:`.env`}),`, below the password line:`]}),_jsx(CodeBlock,{numberOfLines:`2`,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:`2`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`REST_PORT=8790`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),_jsxs(_components.p,{children:[`Then run `,_jsx(_components.code,{children:`docker compose up -d`}),` again. The Console now answers at `,_jsx(_components.code,{children:`http://localhost:8790`}),`, and\nevery `,_jsx(_components.code,{children:`8787`}),` below becomes `,_jsx(_components.code,{children:`8790`}),`. `,_jsx(_components.code,{children:`MCP_HTTP_PORT`}),` moves the agent port the same way. If you\nalready connected `,_jsx(_components.a,{href:`/connect/google`,children:`Google`}),`, `,_jsx(_components.a,{href:`/connect/microsoft`,children:`Microsoft`}),` or\n`,_jsx(_components.a,{href:`/connect/whoop`,children:`WHOOP`}),`, change the address you registered there to the new port. They match it\nexactly, character for character.`]})]}),_jsxs(Warning,{children:[_jsxs(_components.p,{children:[`Docker keeps two storage areas outside the containers: your data, and the key that unlocks every\naccount you connect. `,_jsx(_components.code,{children:`docker compose down -v`}),` deletes both, permanently. Every account you\nconnected becomes unreadable, and nothing can recover them. `,_jsx(_components.code,{children:`docker compose down`}),` without `,_jsx(_components.code,{children:`-v`}),` is\nsafe: it removes the containers and leaves both storage areas alone.`]}),_jsxs(_components.p,{children:[`Take a copy of the key: `,_jsx(_components.a,{href:`/operate/backup-and-export`,children:`Back up, export and erase`}),`. If Postern cannot\nopen the vault at start-up, it refuses to start rather than starting broken. A database restored\nwithout its key stops there.`]})]}),_jsxs(Note,{children:[_jsxs(_components.p,{children:[_jsxs(_components.strong,{children:[`The `,_jsx(_components.code,{children:`VAULT KEY IS NOT BACKED UP`}),` notice.`]}),` Postern prints it in its own log at every start. It\nis a reminder, not an error, and Postern runs anyway. It appears while Postern holds the key\nitself and no marker file sits beside it.`]}),_jsxs(_components.p,{children:[`The notice tells you to acknowledge the copy in the Console. `,_jsx(_components.strong,{children:`No such control ships.`}),` Nothing in\nthe Console writes that marker, and no address answers it. So the notice still appears after you\nhold a good copy.`]}),_jsx(_components.p,{children:`Two things stop it. Take your copy of the key first, then choose one.`}),_jsxs(_components.ol,{children:[`\n`,_jsxs(_components.li,{children:[`\n`,_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Write the marker yourself.`}),` Postern looks for a file named `,_jsx(_components.code,{children:`master.key.backed-up`}),` beside the\nkey. From the folder that holds `,_jsx(_components.code,{children:`docker-compose.yml`}),`:`]}),`\n`,_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:` exec`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` app`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` sh`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -c`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` 'date -u +%Y-%m-%dT%H:%M:%SZ > /app/.pci/master.key.backed-up \u0026\u0026 chmod 600 /app/.pci/master.key.backed-up'`})}),`\n`]})})}),`\n`,_jsx(_components.p,{children:`Postern checks that the file is there. It never reads what is in it. Write the date anyway. It\ntells a later you when you took the copy.`}),`\n`]}),`\n`,_jsxs(_components.li,{children:[`\n`,_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Hold the key yourself.`}),` Put `,_jsx(_components.code,{children:`PCI_MASTER_KEY`}),` in `,_jsx(_components.code,{children:`.env`}),` — a base64 32-byte key you keep —\nand restart. Make one with `,_jsx(_components.code,{children:`openssl rand -base64 32`}),`. Set it before the first start. A new key\nagainst accounts encrypted under an old one makes Postern refuse to start.`]}),`\n`]}),`\n`]}),_jsx(_components.p,{children:`A restore brings the notice back. The marker sat in the old storage area, and the restore makes a\nnew one.`})]}),_jsx(_components.p,{children:`Four commands cover the first week. Run them from the same folder:`}),_jsx(CodeBlock,{filename:``,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:`#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:` ps`}),_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` # is it up`})]}),`\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:` logs`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -f`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` app`}),_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` # read its log`})]}),`\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:` stop`}),_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` # pause it; docker compose start resumes`})]}),`\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:` pull`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` \u0026\u0026 `}),_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:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` # move to a newer image`})]}),`\n`]})})}),_jsxs(_components.p,{children:[`To hold one release instead of the newest, add `,_jsx(_components.code,{children:`POSTERN_VERSION=v0.1.0`}),` to `,_jsx(_components.code,{children:`.env`}),`.`]})]}),_jsxs(Step,{title:`Open the Console`,titleSize:`h2`,id:`open-console`,children:[_jsxs(_components.p,{children:[`The Console answers at `,_jsx(_components.code,{children:`http://localhost:8787`}),`, and only on the machine Postern runs on.`]}),_jsxs(_components.p,{children:[`If Postern runs on the computer in front of you, open\n`,_jsx(_components.a,{href:`http://localhost:8787`,children:`http://localhost:8787`}),`.`]}),_jsxs(_components.p,{children:[`If Postern runs on another computer, forward the Console port to your own computer. Run this `,_jsx(_components.strong,{children:`on\nyour own computer`}),`, not on the machine Postern runs on. Turn on SSH on that machine first.`]}),_jsx(CodeBlock,{filename:``,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:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`ssh`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -N`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -L`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` 127.0.0.1:8787:127.0.0.1:8787`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` YOUR_MACHINE`})]}),`\n`]})})}),_jsxs(_components.p,{children:[`Replace `,_jsx(_components.code,{children:`YOUR_MACHINE`}),` with however you normally reach that computer: a name, a `,_jsx(_components.code,{children:`user@host`}),`, or an\nIP address. The terminal then prints nothing and does not return to a prompt. That is the command at\nwork, not a hang. Leave that window open, use a second terminal window for anything else, and press\n`,_jsx(_components.strong,{children:`Control-C`}),` when you want to stop. Now open `,_jsx(_components.a,{href:`http://localhost:8787`,children:`http://localhost:8787`}),` on your\nown computer.`]}),_jsxs(_components.p,{children:[`Anyone who can reach the Console port can change your settings and create agent keys. It has no\npassword, so it stays on this computer. Do not change the address in `,_jsx(_components.code,{children:`docker-compose.yml`}),` to your\nnetwork. Do not put a tunnel service in front of it. The full argument is in\n`,_jsx(_components.a,{href:`/reference/ports#the-console-has-no-authentication`,children:`The Console has no authentication`}),`.`]}),_jsxs(_components.p,{children:[`The Console opens on a page headed `,_jsx(_components.code,{children:`Your gate is new.`}),` with one button, `,_jsx(_components.strong,{children:`Add your first source`}),`.\nThe left sidebar lists `,_jsx(_components.strong,{children:`Gatehouse`}),`, then the six sectors, then `,_jsx(_components.strong,{children:`Sources`}),`, `,_jsx(_components.strong,{children:`Agents \u0026 keys`}),`,\n`,_jsx(_components.strong,{children:`The ledger`}),` and `,_jsx(_components.strong,{children:`Settings`}),`. A sector is one area of your life: finance, mail, calendar,\ncontacts, health, home. It is the smallest thing you can grant.`]}),_jsx(Frame,{caption:`The Console on a fresh install. The one button reads Add your first source.`,children:_jsx(OptimizedImage,{src:`https://mintcdn.com/postern/E8R3wfogdGSukifu/images/gate-console-new.png?fit=max\u0026auto=format\u0026n=E8R3wfogdGSukifu\u0026q=85\u0026s=4c4a657f0b0bee2e54fd2271094d4613`,alt:`The Postern Console on a fresh install: a page headed “Your gate is new.”, with one button labelled “Add your first source”.`,\"data-og-width\":`1440`,width:`1440`,\"data-og-height\":`900`,height:`900`,\"data-path\":`images/gate-console-new.png`,\"data-optimize\":`true`,\"data-opv\":`3`,srcset:`https://mintcdn.com/postern/E8R3wfogdGSukifu/images/gate-console-new.png?w=280\u0026fit=max\u0026auto=format\u0026n=E8R3wfogdGSukifu\u0026q=85\u0026s=1efd8b78822ad2068f84c62c016826c7 280w, https://mintcdn.com/postern/E8R3wfogdGSukifu/images/gate-console-new.png?w=560\u0026fit=max\u0026auto=format\u0026n=E8R3wfogdGSukifu\u0026q=85\u0026s=d5edc2317a1f20bb0484d17aa1ce30a9 560w, https://mintcdn.com/postern/E8R3wfogdGSukifu/images/gate-console-new.png?w=840\u0026fit=max\u0026auto=format\u0026n=E8R3wfogdGSukifu\u0026q=85\u0026s=f775da38d5b389693090eb5a91e5c8ed 840w, https://mintcdn.com/postern/E8R3wfogdGSukifu/images/gate-console-new.png?w=1100\u0026fit=max\u0026auto=format\u0026n=E8R3wfogdGSukifu\u0026q=85\u0026s=0c0f7e20e2288485f18433a80dcf59d1 1100w, https://mintcdn.com/postern/E8R3wfogdGSukifu/images/gate-console-new.png?w=1650\u0026fit=max\u0026auto=format\u0026n=E8R3wfogdGSukifu\u0026q=85\u0026s=b6e55c4f4a735c41b9e34e49c1a5d4bb 1650w, https://mintcdn.com/postern/E8R3wfogdGSukifu/images/gate-console-new.png?w=2500\u0026fit=max\u0026auto=format\u0026n=E8R3wfogdGSukifu\u0026q=85\u0026s=2c7ce9daa077c422409f2b995d646c19 2500w`})}),_jsxs(_components.p,{children:[`To let an agent reach Postern from another device, see `,_jsx(_components.a,{href:`/start/remote-access`,children:`Remote access`}),`. It is\noff by default, and it never moves the Console off this machine.`]})]})]}),`\n`,_jsx(Heading,{level:`2`,id:`confirm-it-works`,children:`Confirm it works`}),`\n`,_jsx(_components.p,{children:`Run these on the machine Postern runs on.`}),`\n`,_jsx(CodeBlock,{filename:``,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:`#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:` ps`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`curl`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -s`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` localhost:8787/healthz`})]}),`\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:` volume`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` ls`})]}),`\n`]})})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`docker compose ps`}),` lists `,_jsx(_components.code,{children:`db`}),` and `,_jsx(_components.code,{children:`app`}),` as `,_jsx(_components.code,{children:`running`}),`, and `,_jsx(_components.code,{children:`app`}),` turns `,_jsx(_components.code,{children:`healthy`}),` about 20 seconds\nafter that.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`curl -s localhost:8787/healthz`}),` prints `,_jsx(_components.code,{children:`{\"status\":\"ok\"}`}),`. The same address in a browser shows the\nsame thing.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`docker volume ls`}),` lists two names that end in `,_jsx(_components.code,{children:`pgdata`}),` and `,_jsx(_components.code,{children:`pci_master_key`}),`.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`.env`}),` sits beside `,_jsx(_components.code,{children:`docker-compose.yml`}),` and holds one line that begins `,_jsx(_components.code,{children:`POSTGRES_PASSWORD=`}),`.`]}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`if-something-went-wrong`,children:`If something went wrong`}),`\n`,_jsxs(Table,{children:[_jsx(_components.thead,{children:_jsxs(_components.tr,{children:[_jsx(_components.th,{children:`What you see`}),_jsx(_components.th,{children:`What to do`})]})}),_jsxs(_components.tbody,{children:[_jsxs(_components.tr,{children:[_jsxs(_components.td,{children:[_jsx(_components.code,{children:`docker compose up -d`}),` stops at once and prints `,_jsx(_components.code,{children:`set POSTGRES_PASSWORD in .env — generate one with: openssl rand -hex 24`})]}),_jsxs(_components.td,{children:[_jsx(_components.code,{children:`.env`}),` is missing or empty. Run the `,_jsx(_components.a,{href:`#password`,children:`step 2`}),` command again, in the folder that holds `,_jsx(_components.code,{children:`docker-compose.yml`}),`.`]})]}),_jsxs(_components.tr,{children:[_jsxs(_components.td,{children:[_jsx(_components.code,{children:`app`}),` starts and exits within seconds, and your password holds a character that is not a letter or a digit`]}),_jsxs(_components.td,{children:[`Run the `,_jsx(_components.a,{href:`#password`,children:`step 2`}),` command again, then `,_jsx(_components.code,{children:`docker compose down -v \u0026\u0026 docker compose up -d`}),`. That wipes the database, which is safe only before you connect anything.`]})]}),_jsxs(_components.tr,{children:[_jsxs(_components.td,{children:[_jsx(_components.code,{children:`db`}),` is up, `,_jsx(_components.code,{children:`app`}),` cannot sign in to the database, and `,_jsx(_components.code,{children:`.env`}),` changed since the first start`]}),_jsxs(_components.td,{children:[`Postgres kept the first password. Put the original line back in `,_jsx(_components.code,{children:`.env`}),`, then run `,_jsx(_components.code,{children:`docker compose up -d`}),`.`]})]}),_jsxs(_components.tr,{children:[_jsxs(_components.td,{children:[_jsx(_components.code,{children:`app`}),` does not start, and another program on this computer already uses 8787 or 8788`]}),_jsxs(_components.td,{children:[`Move Postern’s port. `,_jsx(_components.a,{href:`#start`,children:`Step 3`}),` carries the line to add.`]})]}),_jsxs(_components.tr,{children:[_jsxs(_components.td,{children:[`The browser shows a not-found page while `,_jsx(_components.code,{children:`curl -s localhost:8787/healthz`}),` still answers `,_jsx(_components.code,{children:`{\"status\":\"ok\"}`})]}),_jsxs(_components.td,{children:[`Postern runs, and its Console files are not where it looked. Run `,_jsx(_components.code,{children:`docker compose logs app`}),`; the log names the folder it tried. Set `,_jsx(_components.code,{children:`PCI_CONSOLE_DIR`}),` in `,_jsx(_components.code,{children:`.env`}),` to the right folder.`]})]}),_jsxs(_components.tr,{children:[_jsxs(_components.td,{children:[`The log prints `,_jsx(_components.code,{children:`VAULT KEY IS NOT BACKED UP`}),` at every start`]}),_jsxs(_components.td,{children:[`That is the reminder, not a fault. `,_jsx(_components.a,{href:`#start`,children:`Step 3`}),` has the two ways to stop it.`]})]})]})]}),`\n`,_jsx(Heading,{level:`2`,id:`what-you-have-now`,children:`What you have now`}),`\n`,_jsxs(_components.p,{children:[`Postern runs on a machine you own. Docker downloaded a finished image from\n`,_jsx(_components.code,{children:`ghcr.io/getpostern/postern`}),` and built nothing on that machine. There is no Postern account and no\nPostern server in the middle. You have connected nothing yet, and no agent can reach anything until\nyou grant it.`]}),`\n`,_jsxs(_components.p,{children:[`Three things are now yours to keep: the machine, on and updated; the one line in `,_jsx(_components.code,{children:`.env`}),`; and a copy\nof the vault key.`]}),`\n`,_jsx(Heading,{level:`2`,id:`next`,children:`Next`}),`\n`,_jsxs(Columns,{cols:2,children:[_jsx(Card,{title:`Mint your first key`,href:`/start/first-key`,children:_jsx(_components.p,{children:`a name, the sectors it opens, a deadline · a few minutes · Postern shows the key once`})}),_jsx(Card,{title:`Set up remote access`,href:`/start/remote-access`,children:_jsx(_components.p,{children:`Tailscale, and Funnel only if a hosted agent needs a public address · about 20 minutes, once ·\noff by default`})})]})]})}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":{"codeStyling":"system","config":{},"pageMetadata":{"title":"Install Postern","description":"Run Postern under Docker on a machine you own, then open its Console.","href":"/start/install"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Choose your path","slug":"choose-your-path","depth":2,"children":[]},{"title":"Get the compose file","slug":"get-file","depth":2,"children":[]},{"title":"Write the database password","slug":"password","depth":2,"children":[]},{"title":"Start Postern","slug":"start","depth":2,"children":[]},{"title":"Open the Console","slug":"open-console","depth":2,"children":[]},{"title":"Confirm it works","slug":"confirm-it-works","depth":2,"children":[]},{"title":"If something went wrong","slug":"if-something-went-wrong","depth":2,"children":[]},{"title":"What you have now","slug":"what-you-have-now","depth":2,"children":[]},{"title":"Next","slug":"next","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"subdomain":"postern","siteName":"Postern","title":"Install Postern - Postern","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-08-07T18:52:32.603Z","originalFileLocation":"start/install.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=\"light\"}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=\"Run Postern under Docker on a machine you own, then open its Console.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Postern\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/postern/qaGaM2kl3nxvvGFE/_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/postern/qaGaM2kl3nxvvGFE/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/postern/qaGaM2kl3nxvvGFE/_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/postern/qaGaM2kl3nxvvGFE/_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/postern/qaGaM2kl3nxvvGFE/_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/postern/qaGaM2kl3nxvvGFE/_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/postern/qaGaM2kl3nxvvGFE/_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/postern/qaGaM2kl3nxvvGFE/_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/postern/qaGaM2kl3nxvvGFE/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Install Postern - Postern\">\u003cmeta data-mint-head property=\"og:description\" content=\"Run Postern under Docker on a machine you own, then open its Console.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Postern\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.getpostern.com/start/install\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://postern.mintlify.site/_mintlify/api/og/v2?division=Start+here\u0026amp;title=Install+Postern\u0026amp;description=Run+Postern+under+Docker+on+a+machine+you+own%2C+then+open+its+Console.\u0026amp;theme=4d380beff7db0b8a6a5f24a8\u0026amp;signature=a73e477e3546d36379cc58e42a0888684c977ffea3a5337812543f6b72e4b298\">\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=\"Install Postern - Postern\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Run Postern under Docker on a machine you own, then open its Console.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://postern.mintlify.site/_mintlify/api/og/v2?division=Start+here\u0026amp;title=Install+Postern\u0026amp;description=Run+Postern+under+Docker+on+a+machine+you+own%2C+then+open+its+Console.\u0026amp;theme=4d380beff7db0b8a6a5f24a8\u0026amp;signature=a73e477e3546d36379cc58e42a0888684c977ffea3a5337812543f6b72e4b298\">\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/postern/qaGaM2kl3nxvvGFE/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Postern\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#E7A42A\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.getpostern.com/start/install\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/start/install.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.getpostern.com/#organization\",\"name\":\"Postern\",\"url\":\"https://docs.getpostern.com\"},{\"@type\":\"WebSite\",\"@id\":\"https://docs.getpostern.com#website\",\"name\":\"Postern\",\"url\":\"https://docs.getpostern.com\",\"publisher\":{\"@id\":\"https://docs.getpostern.com/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.getpostern.com/start/install#webpage\",\"url\":\"https://docs.getpostern.com/start/install\",\"name\":\"Install Postern\",\"description\":\"Run Postern under Docker on a machine you own, then open its Console.\",\"dateModified\":\"2026-08-07T18:52:32.603Z\",\"isPartOf\":{\"@id\":\"https://docs.getpostern.com#website\"},\"breadcrumb\":{\"@id\":\"https://docs.getpostern.com/start/install#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.getpostern.com/start/install#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Start here\",\"item\":\"https://docs.getpostern.com/index\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Install Postern\",\"item\":\"https://docs.getpostern.com/start/install\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://docs.getpostern.com/start/install#article\",\"headline\":\"Install Postern\",\"name\":\"Install Postern\",\"description\":\"Run Postern under Docker on a machine you own, then open its Console.\",\"url\":\"https://docs.getpostern.com/start/install\",\"mainEntityOfPage\":{\"@id\":\"https://docs.getpostern.com/start/install#webpage\"},\"dateModified\":\"2026-08-07T18:52:32.603Z\",\"publisher\":{\"@id\":\"https://docs.getpostern.com/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.getpostern.com#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Postern\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"Postern","nav":{"global":null,"groups":[{"group":"Start here","pages":[{"title":"Postern","description":"A personal context gateway you run yourself. AI assistants ask Postern; Postern answers from your own machine.","href":"/index"},{"title":"Install Postern","href":"/start/install"},{"title":"Set up remote access","description":"Open two ports to your own devices, and one port to the internet only if claude.ai or ChatGPT needs it.","href":"/start/remote-access"},{"title":"Mint your first key","href":"/start/first-key"},{"title":"Connect an agent","href":"/start/connect-an-agent"},{"title":"Set up with an agent","href":"/start/set-up-with-an-agent"}]},{"group":"Connect a source","pages":[{"group":"Money","pages":[{"title":"Connect SimpleFIN","href":"/connect/simplefin"},{"title":"Connect Plaid","href":"/connect/plaid"}]},{"group":"Health","pages":[{"title":"Connect Apple Health","href":"/connect/apple-health"},{"title":"Connect WHOOP","href":"/connect/whoop"}]},{"group":"Mail, calendar \u0026 contacts","pages":[{"title":"Connect Google and Gmail","href":"/connect/google"},{"title":"Connect Microsoft","href":"/connect/microsoft"},{"title":"Connect iCloud","href":"/connect/icloud"}]},{"group":"Home","pages":[{"title":"Connect Home Assistant","href":"/connect/home-assistant"}]},{"group":"Agents","pages":[{"title":"Connect ChatGPT","href":"/connect/chatgpt"}]}]},{"group":"Operate","pages":[{"title":"Back up, export and erase","href":"/operate/backup-and-export"}]},{"group":"Reference","pages":[{"group":"Security","pages":[{"title":"Security model","href":"/reference/security-model"},{"title":"Which computers can reach Postern","href":"/reference/ports"},{"title":"The vault and the master key","href":"/reference/vault"},{"title":"Provider sign-ins","href":"/reference/provider-sign-ins"}]},{"group":"Agents, keys and grants","pages":[{"title":"Grants and sectors","href":"/reference/grants-and-sectors"},{"title":"Agent keys: create, expire, revoke, rotate","href":"/reference/agent-keys"},{"title":"Why an agent was refused","href":"/reference/refusals"}]},{"group":"MCP tools","pages":[{"title":"MCP tools","href":"/reference/mcp-primitives"},{"title":"Read data: get_schema, query, get_record","href":"/reference/mcp-reading"},{"title":"Freshness and live reads: describe_context, fetch_live","href":"/reference/mcp-freshness"},{"title":"Act on a source: list_actions, invoke_action","href":"/reference/mcp-actions"},{"title":"The OAuth bridge, for hosted agents","href":"/reference/oauth-bridge"}]},{"group":"The record","pages":[{"title":"The audit log","href":"/reference/audit-log"},{"title":"Read the record","href":"/reference/read-the-record"},{"title":"What the record cannot promise","href":"/reference/record-limits"}]}]},{"group":"About","pages":[{"title":"Frequently asked questions","href":"/about/faq"}]}]},"basePath":"","title":"Install Postern - Postern","buildId":"dpl_8LJbrAiZvr1K98WQNZXCyY5AvT34","deploymentId":"dpl_8LJbrAiZvr1K98WQNZXCyY5AvT34","dynamics":["07c022d89b","a74be98ba7"],"chunks":["Frame-NdetNziF.js","DynamicMarkdown-CD8713Yy.js","zoom-image-lightbox-DMoL6lHz.js","lightbox-handle-r17bVUz3.js"]}