Can I self-host Raycast?
YES · ONE COMMAND— setup effort 1 of 4YES — it's called IT Tools. It takes one prompt, a 256 MB VPS, and about 6 minutes. That is $8 a month you stop paying Raycast — $96 a year on the Pro plan, 1 seat assumed.
Why people pay for Raycast
Stated as the vendor would want it stated. A replacement you pick without knowing what the subscription actually buys is a replacement you abandon in a fortnight.
Raycast sells a fast launcher and a library of extensions that sit in front of the workday on macOS: clipboard history, snippets, AI, and team features that feel worth a few dollars when the alternative is a pile of single-purpose utilities.
| Plan | List price | What it buys |
|---|---|---|
| Free | free | Core launcher free. |
| Prothe plan this page prices against | $8/mo per seat | Pro features including cloud sync and AI limits; confirm on raycast.com/pricing. |
Vendor list prices in USD, read from the pricing page on 2026-08-07 · confidence: medium
Replaced by IT Tools
One project, named before the prompt, so you know what you are about to install.
A chest of offline-friendly developer utilities in the browser: encoders, converters, generators and more.
A self-hosted chest of encoder/converter/generator utilities so secrets never leave your browser for a random website. It is not a Raycast replacement for window management or extensions; it is the honest swap for the web-tool half of the habit.
The swap
You'd run
IT Tools
ONE COMMAND · ~6 min to running · 256 MB RAM
Raycast Pro · 1 seat assumed · vendor list price · checked 2026-08-07 · source · confidence: medium
Before you start
- RAM floor
- 256 MBfloor from upstream docs — not measured by us yet
- Disk
- 2 GBthe app, its data, and room for one backup
- Domain needed
- yes, one A recorda hostname pointed at the box before you start — TLS needs it on the cloud path, and the local path needs none
- Time budget
- ~6 minunder 10 minutes, through the first backup
The prompt
Two paths to the same IT Tools: the cloud one assumes Prompt Zero is done on a server you rent, the local one assumes nothing but a computer that can run Docker Desktop. Read whichever you pick before you paste it, which is the whole reason both are on the page instead of behind a download.
Where it runs
247 lines · 10,949 bytes
What this prompt will do
- Preflight
- Layout
- Secrets
- compose.yml
- Caddy and TLS
- Firewall
- Start and verify
- First backup and restore
- Updating later
- What will probably go wrong
- Out of scope
Read out of the prompt’s own step headings at build time — if the prompt changes, this list changes with it.
You are Claude Code on the user's machine. The user has completed Prompt Zero: `ssh vps` works,
Docker and Caddy are installed, the firewall is default-deny.
Run every command in this prompt on the server over `ssh vps` unless the step says otherwise.
Install IT Tools 2024.10.22-7ca5933 on that server, reachable at https://<DOMAIN>, behind the
existing Caddy with automatic TLS.
## 1. Preflight
If `<DOMAIN>` is still literal, ask the user for the hostname once and stop until they answer.
Its A record must already point at this server. Say two things when you ask. One: this produces
a public utility page. There is no login, no account and no first-run wizard, and every tool on
the page is usable by anyone who loads the URL. Two: that is intentional (same posture as a
public status page), and Caddy `basic_auth` is the documented opt-in if they would rather put a
password in front; this install does not enable it.
IT Tools needs 256 MB of RAM available and 2 GB free on /srv. The image is a static web UI and
costs almost nothing at idle. The pinned tag publishes amd64 and arm64. Measure:
```bash
free -m | awk '/^Mem:/ {print $7 " MB available of " $2 " MB"}'
df -BG --output=avail /srv | tail -1
dpkg --print-architecture
dig +short <DOMAIN>
```
If available RAM is under 256 MB or free disk is under 2 GB, print both numbers and stop. Do not
install and hope. If `dig +short` prints nothing, print that and stop: Caddy cannot certify a
hostname that does not resolve.
Also say once: the pinned image tag `2024.10.22-7ca5933` was published on 2024-10-22, about 21
months before the 2026-08-07 check that recorded this pin. Upstream release cadence is slow; a
`nightly` tag exists on Docker Hub and is not what this install uses. Block 9 covers how to move
forward when a newer stable identity appears.
## 2. Layout
```bash
sudo install -d -m 750 -o $(id -u) -g $(id -g) /srv/it-tools /srv/it-tools/backups
ls -la /srv/it-tools
```
Assert: `ls -la` shows `backups` owned by the login user. There is no `data/` directory and that
is not an omission: the container serves a static UI, writes no application database, and keeps
no server-side accounts. Bookmarks and "recent tools" live in the browser if at all.
## 3. Secrets
No secret is generated for this install and there is no `.env` file. That is not an oversight,
and there is no default credential for step 7 to close: IT Tools ships no account, no
registration form and no administration screen. There is no claim race, because there is nothing
to claim.
What replaces the credential question here is publication. The page answers everybody, because
that is what a shared utility chest on a URL is for. Tell the user this: if they would rather
the page were behind a password, Caddy's `basic_auth` directive on the site block does that, this
install uses neither basic_auth nor an upstream login, and turning basic_auth on is a decision
about who the page is for. Do not invent a first-run setup step.
## 4. compose.yml
```bash
cat > /srv/it-tools/compose.yml <<'EOF'
# IT Tools · the deterministic fallback. Authored by caniselfhostit from the
# upstream documentation, not copied from a repository:
# docker run ......... https://github.com/CorentinTh/it-tools#self-host
#
# One service. Static web UI served by the image; no application database, no
# accounts, no env secrets. Upstream publishes docker run lines rather than a
# maintained multi-service compose file (officialCompose: none).
# Tag 2024.10.22-7ca5933 was published 2024-10-22; digest read from Docker Hub
# on 2026-08-07. See block 9 of the prompts for the age of this pin.
#
# NOT YET VERIFIED: no harness run has been recorded against this file.
services:
it-tools:
image: corentinth/it-tools:2024.10.22-7ca5933@sha256:8b8128748339583ca951af03dfe02a9a4d7363f61a216226fc28030731a5a61f
container_name: it-tools
restart: unless-stopped
ports:
# Loopback only: the host's Caddy is the only thing that reaches 8209.
- "127.0.0.1:8209:80"
EOF
cd /srv/it-tools && docker compose config >/dev/null && echo "compose OK"
```
Assert: that prints `compose OK`. One service, one published port, no volumes, no env_file. Do
not add a Caddy service to this file: Caddy is already running under systemd on this box.
## 5. Caddy and TLS
Write the site block under `/srv/it-tools/Caddyfile`, then append it to the live Caddyfile with
`<DOMAIN>` replaced. Copy the live file first: a syntax error here takes down every other site
on the box.
```bash
cat > /srv/it-tools/Caddyfile <<'EOF'
# IT Tools · the Caddy site block for this service.
#
# Authored by caniselfhostit from
# https://caddyserver.com/docs/automatic-https
#
# Append this to /etc/caddy/Caddyfile, the Caddy that Prompt Zero installed, with
# <DOMAIN> replaced by the hostname pointed at this box. Everything served here
# is public by design: there are no accounts. Caddy basic_auth is an opt-in if
# you would rather the URL not be world-readable.
<DOMAIN> {
encode zstd gzip
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
Referrer-Policy "no-referrer"
-Server
}
# 8209 is the loopback port compose publishes on this host. It is not a
# container port and it is not open in the firewall.
reverse_proxy 127.0.0.1:8209
}
EOF
sudo cp /etc/caddy/Caddyfile /etc/caddy/Caddyfile.before-it-tools
printf '\n' | sudo tee -a /etc/caddy/Caddyfile >/dev/null
DOMAIN_HOST=<DOMAIN>
sed "s|<DOMAIN>|${DOMAIN_HOST}|g" /srv/it-tools/Caddyfile | sudo tee -a /etc/caddy/Caddyfile >/dev/null
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
```
Set `DOMAIN_HOST` to the real hostname from step 1 before running `sed`. Assert: `caddy validate`
exits 0 and the reload exits 0. If validate fails, restore
`/etc/caddy/Caddyfile.before-it-tools`, reload, and report what it objected to. Caddy requests
the certificate on the first request and renews it on its own.
If the user later wants a password on the URL, the opt-in is a `basic_auth` directive inside the
site block (Caddy docs), with a hash generated by `caddy hash-password`, then validate and
reload. Do not add that unless they ask after understanding the page is public today.
## 6. Firewall
Two ports open, both Caddy's:
```bash
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 443/udp
sudo ufw status verbose
```
80/tcp answers the ACME challenge and redirects to HTTPS, 443/tcp is the only way in, and 443/udp
is HTTP/3. 8209 stays closed because compose binds it to 127.0.0.1. Assert: `ufw status verbose`
prints `Status: active`, shows 80, 443/tcp and 443/udp, and no rule mentioning 8209.
## 7. Start and verify
```bash
cd /srv/it-tools
docker compose pull
docker compose up -d
for i in $(seq 1 24); do code=$(curl -sS -o /dev/null -w '%{http_code}' https://<DOMAIN>/); echo "$i $code"; [ "$code" = 200 ] && break; sleep 5; done
curl -sSL https://<DOMAIN>/ | grep -ciE 'it-tools|IT Tools|token|encode|hash|Base64'
docker compose ps
```
Assert all of the following, and print what you received. The loop ends printing `200`. The
grep count is greater than `0` because the UI names tools or the product. If Caddy returns 502
with a running container, step 5 is the likely cause. A running container is not success.
There is no sign-in to complete and no wizard to finish. The security posture here is consent to
publication, not a closed door.
STOP: tell the user to open https://<DOMAIN> in a private window, try one tool (for example a
Base64 encode of a short string), and confirm two things back to you: that the page loads
without a login, and that they are content for anyone who can guess or learn the hostname to use
the same tools, because that is now true. Do not continue until they confirm.
## 8. First backup and restore
This install is nearly stateless. The archive is the compose file and the live Caddy site block.
There is no application `data/` directory to include, and inventing one would back up empty
space. Say that plainly to the user.
```bash
cd /srv/it-tools
sudo tar -czf /srv/it-tools/backups/it-tools-$(date +%F).tar.gz \
-C /srv/it-tools compose.yml \
-C /etc/caddy Caddyfile
ls -lh /srv/it-tools/backups/
```
Assert: the archive exists and is non-empty. Print its size. There is no need to stop the
container for this backup: nothing under `/srv/it-tools` is being written by the app. Never
append `|| true` to this tar.
From the user's machine:
```bash
mkdir -p ~/backups/it-tools
scp vps:/srv/it-tools/backups/*.tar.gz ~/backups/it-tools/
```
To restore: put `compose.yml` back under `/srv/it-tools`, put the Caddy block back if that is
what was lost, `docker compose up -d`. Losing the image pin costs a careful re-edit of compose;
losing the Caddy block costs the site name on this box. There is no user content on the server to
lose, which is the honest upside of a stateless UI.
## 9. Updating later
Releases: https://github.com/CorentinTh/it-tools/releases. The image tags follow the calver-style
identity upstream publishes (for example `2024.10.22-7ca5933`). **This pin is old on purpose
until a newer stable identity is verified:** the tag was published 2024-10-22, about 21 months
before the 2026-08-07 digest check. Docker Hub also carries `nightly`; do not float to nightly
from this prompt. When a newer release tag exists that you trust, take a backup of compose and
Caddy, edit the image line to the new tag and digest, then:
```bash
cd /srv/it-tools
docker compose pull
docker compose up -d
docker compose logs --tail 20 it-tools
```
Re-run step 7's curl and body grep before calling the update done. Because tools run in the
browser against the JS you just shipped, treat upgrades like any dependency that will see secrets
users paste into forms.
## 10. What will probably go wrong
You will paste a production secret into a tool on this host, close the tab, and later wonder who
else loaded the same URL. The tools are client-side, which is why self-hosting beats a random
website, but the hostname is still public without basic_auth. I treated it like a private lab
once and shared the link in a chat; the fix was either basic_auth or a hostname nobody else had.
The second failure mode is assuming the pin is "current" because the container is healthy: the
tag can be more than a year old and still run fine. Check releases before you need a fix that
only exists upstream.
## 11. Out of scope
- Do not add a Caddy container to the compose file. Caddy already runs under systemd on this box.
- Do not publish 8209 on `0.0.0.0` or open it in the firewall.
- Do not invent accounts, a first-run wizard, or a claim-race warning for software that has none.
- Do not add basic_auth unless the user explicitly asks after the public-by-design consent stop.
- Do not switch the image to `latest` or `nightly` without a deliberate pin decision.No terminal agent? Use the chat fallback — slower, you paste the commands
For ChatGPT or Claude in a browser. The model cannot touch your server, so it hands you one command at a time and you run each one. Same install, more of your evening.
This path is slower: you paste every command yourself, and there is nobody watching the output
but you. If you can run Claude Code, use the other tab.
You are installing IT Tools 2024.10.22-7ca5933 on a VPS where Prompt Zero is done: `ssh vps`
works, Docker and Caddy are installed, the firewall is default-deny. Run everything over
`ssh vps` unless a step says otherwise, and replace `<DOMAIN>` with the hostname whose A record
already points at the box.
Read these before step 1. This produces a public utility page: no login, no account, no first-run
wizard, and every tool is usable by anyone who loads the URL. That is intentional. Caddy
`basic_auth` is the opt-in if you want a password in front; this install does not enable it.
There is no application data directory. The pinned tag was published 2024-10-22 (about 21 months
before the 2026-08-07 digest check); upstream cadence is slow, and `nightly` is not this pin.
## 1. Preflight
```bash
free -m | awk '/^Mem:/ {print $7 " MB available of " $2 " MB"}'
df -BG --output=avail /srv | tail -1
dpkg --print-architecture
dig +short <DOMAIN>
```
You should see: at least `256` MB available, at least `2` G free, `amd64` or `arm64`, and your
server's IP. If dig is empty, add the A record and wait.
## 2. Layout
```bash
sudo install -d -m 750 -o $(id -u) -g $(id -g) /srv/it-tools /srv/it-tools/backups
ls -la /srv/it-tools
```
You should see `backups` under `/srv/it-tools`. There is no `data/` directory: the container is a
stateless static UI. Bookmarks live in the browser if at all.
## 3. Secrets
No secret is generated and there is no `.env` file. There is no claim race because there is
nothing to claim. If you want a password on the URL later, use Caddy `basic_auth` with
`caddy hash-password`, then validate and reload. Do not invent a first-run setup step for
software that has none.
## 4. compose.yml
```bash
cat > /srv/it-tools/compose.yml <<'EOF'
# IT Tools · the deterministic fallback. Authored by caniselfhostit from the
# upstream documentation, not copied from a repository:
# docker run ......... https://github.com/CorentinTh/it-tools#self-host
#
# One service. Static web UI served by the image; no application database, no
# accounts, no env secrets. Upstream publishes docker run lines rather than a
# maintained multi-service compose file (officialCompose: none).
# Tag 2024.10.22-7ca5933 was published 2024-10-22; digest read from Docker Hub
# on 2026-08-07. See block 9 of the prompts for the age of this pin.
#
# NOT YET VERIFIED: no harness run has been recorded against this file.
services:
it-tools:
image: corentinth/it-tools:2024.10.22-7ca5933@sha256:8b8128748339583ca951af03dfe02a9a4d7363f61a216226fc28030731a5a61f
container_name: it-tools
restart: unless-stopped
ports:
# Loopback only: the host's Caddy is the only thing that reaches 8209.
- "127.0.0.1:8209:80"
EOF
cd /srv/it-tools && docker compose config >/dev/null && echo "compose OK"
```
You should see `compose OK`. One service, no volumes, no env_file. Do not add a Caddy service
here. Upstream's docs show `docker run` lines; this compose is our deterministic wrapper around
that image (officialCompose: none; upstream publishes no compose stack).
## 5. Caddy and TLS
```bash
cat > /srv/it-tools/Caddyfile <<'EOF'
# IT Tools · the Caddy site block for this service.
#
# Authored by caniselfhostit from
# https://caddyserver.com/docs/automatic-https
#
# Append this to /etc/caddy/Caddyfile, the Caddy that Prompt Zero installed, with
# <DOMAIN> replaced by the hostname pointed at this box. Everything served here
# is public by design: there are no accounts. Caddy basic_auth is an opt-in if
# you would rather the URL not be world-readable.
<DOMAIN> {
encode zstd gzip
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
Referrer-Policy "no-referrer"
-Server
}
# 8209 is the loopback port compose publishes on this host. It is not a
# container port and it is not open in the firewall.
reverse_proxy 127.0.0.1:8209
}
EOF
sudo cp /etc/caddy/Caddyfile /etc/caddy/Caddyfile.before-it-tools
printf '\n' | sudo tee -a /etc/caddy/Caddyfile >/dev/null
DOMAIN_HOST=<DOMAIN>
sed "s|<DOMAIN>|${DOMAIN_HOST}|g" /srv/it-tools/Caddyfile | sudo tee -a /etc/caddy/Caddyfile >/dev/null
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
```
Set `DOMAIN_HOST` to your real hostname before sed. `caddy validate` and reload must both exit
0. If validate fails, restore `/etc/caddy/Caddyfile.before-it-tools`, reload, and fix the syntax.
## 6. Firewall
```bash
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 443/udp
sudo ufw status verbose
```
You should see Status active, 80 and 443 open, nothing for 8209. 8209 stays closed because
compose binds it to 127.0.0.1.
## 7. Start and verify
```bash
cd /srv/it-tools
docker compose pull
docker compose up -d
for i in $(seq 1 24); do code=$(curl -sS -o /dev/null -w '%{http_code}' https://<DOMAIN>/); echo "$i $code"; [ "$code" = 200 ] && break; sleep 5; done
curl -sSL https://<DOMAIN>/ | grep -ciE 'it-tools|IT Tools|token|encode|hash|Base64'
docker compose ps
```
You should see the loop end with `200` and a body grep count greater than `0`. If you get 502,
re-check step 5. There is no sign-in to complete and no wizard to finish. The security posture is
consent to publication, not a closed account door.
STOP: open https://<DOMAIN> in a private window, try one tool (for example Base64 encode), and
confirm two things: the page loads without a login, and you are content for anyone who can reach
the hostname to use the same tools. Do not continue until they confirm.
## 8. First backup and restore
Nearly stateless. Archive compose.yml and the live Caddyfile only. Do not invent an empty
`data/` directory to tar.
```bash
cd /srv/it-tools
sudo tar -czf /srv/it-tools/backups/it-tools-$(date +%F).tar.gz \
-C /srv/it-tools compose.yml \
-C /etc/caddy Caddyfile
ls -lh /srv/it-tools/backups/
```
The archive must exist and be non-empty; print its size. No need to stop the container. From your
laptop:
```bash
mkdir -p ~/backups/it-tools
scp vps:/srv/it-tools/backups/*.tar.gz ~/backups/it-tools/
```
To restore: put compose back under `/srv/it-tools`, put the Caddy block back if needed,
`docker compose up -d`. There is no user content on the server to lose. Browser form history is
not part of this backup.
## 9. Updating later
Releases: https://github.com/CorentinTh/it-tools/releases. **This pin is old until you move it:**
tag `2024.10.22-7ca5933` was published 2024-10-22, about 21 months before the 2026-08-07 check.
Docker Hub also has `nightly`; do not float to nightly from this path. When a newer stable tag
exists that you trust, backup compose and Caddy, edit the image line to the new tag and digest:
```bash
cd /srv/it-tools
docker compose pull
docker compose up -d
docker compose logs --tail 20 it-tools
```
Re-run step 7's curl and body grep. Treat upgrades like any dependency that will see secrets
users paste into forms.
## 10. What will probably go wrong
You will paste a production secret into a tool on this host, share the hostname in a chat, and
later wonder who else used the same URL. Client-side execution beats a random website; a public
hostname without basic_auth is still public. Second: assuming the pin is current because the
container is healthy. Check releases before you need a fix that only exists upstream. Third: a
corporate content filter that blocks tool paths on your own domain; fix the filter, not the
image pin.
## 11. Out of scope
- Do not add a Caddy container to compose. Caddy already runs under systemd on this host.
- Do not publish 8209 on `0.0.0.0` or open it in the firewall.
- Do not invent accounts, a first-run wizard, or a claim-race warning for software that has none.
- Do not add basic_auth unless you explicitly decide after the public-by-design consent stop.
- Do not switch the image to `latest` or `nightly` without a deliberate pin decision.
Honesty about Raycast: this install replaces the habit of pasting into random web utilities. It
does not replace a launcher, clipboard history, window management or an extension host. If you
came from a Raycast Pro comparison, keep that distinction clear.
Hostname discipline: the Caddy site name and the browser URL must match. Publication discipline:
private-window the page once as a stranger. State discipline: there is almost no state; do not
fake a data volume. Pin discipline: record the next digest when you move off 2024.10.22-7ca5933.
If a step's assert fails, name the earlier step before changing anything else. Preflight is step
1. Compose errors are step 4. Certificate or 502 problems are step 5. Open ports that should be
closed are step 6. A 200 with a blank or wrong body is step 4 or 7. Empty backups are step 8.
NOT YET VERIFIED: no harness run has been recorded against this install path.
Client-side trust model: most tools transform text in the browser. That means the server you
just installed is mainly a delivery vehicle for JavaScript. Self-hosting still matters because
you control which JS bundle runs and who can reach it, but it is not a magical air gap. Keep the
image pin intentional, and prefer basic_auth or a private hostname when the tools will see
employer secrets.
What this does not replace: a password manager, a notes app, a snippet sync service, or Raycast
as a launcher. If a comparison page ranked IT Tools against Raycast Pro, the honest overlap is
only the "open a web utility" habit. Keep paying for a launcher if that is the job you hired
Raycast for.
Operational rhythm: after install, open the page once a week for a month so the bookmark sticks.
If you never open it, turn the container off and reclaim the RAM. If you open it daily, consider
basic_auth or a long random subdomain so search engines and curious scanners are less likely to
find a toolbox full of crypto toys on your domain.
Caddy basic_auth sketch (opt-in only, not part of this install unless you ask for it after the
consent stop):
# generate a hash on the server
caddy hash-password
# then inside the site block, before reverse_proxy:
# basic_auth {
# <username> <hash>
# }
Validate and reload after any Caddy edit. A syntax error takes down every site on the box, which
is why step 5 copies the live file first.
When curl prints 000 or times out, check DNS, ufw, and `docker compose ps` in that order. When
curl prints 502, check that 8209 is listening on 127.0.0.1 and that Caddy's reverse_proxy target
matches. When curl prints 200 but the body grep is zero, you may be hitting a different service
on the same hostname; inspect the Caddyfile for duplicate site blocks.
Keep the off-box copy of the compose+Caddy archive if you care about recovering the pin and the
hostname wiring. The tools themselves are the image: a `docker compose pull` of the same pin
recreates the UI without restore.
This path is NOT YET VERIFIED on a clean harness machine; treat the asserts as the contract and
stop when they fail.
Mobile browsers will load the same UI if the hostname is public. That is convenient for a phone
on LTE and also means a pocket device is one link away from the same toolbox. If that is too
broad, basic_auth or VPN-only access is the fix, not hoping nobody bookmarks the URL.
Logging: the container logs little about what users paste, which is good. Your reverse proxy
access logs still record who hit the hostname. If access logs are a problem for your threat
model, tighten log retention on the host Caddy, not inside this compose file.
Disk: the image layer is the main disk cost. The backup archive is tiny. Do not allocate a large
volume "just in case"; there is no growing database here.
IPv6: if the AAAA record points somewhere else, browsers may hit the wrong box. Keep DNS boring:
one A record to this VPS unless you know you want dual-stack.
Finally, re-read the consent stop after any change that makes the hostname easier to guess
(short vanity names, public docs that advertise the tools URL). Public by design is a choice you
can reverse with basic_auth; it is harder to reverse a secret that already left the browser.222 lines · 9,553 bytes
What this prompt will do
- Preflight
- Docker
- Layout
- Secrets
- compose.yml
- Nothing is public
- Start and verify
- First backup and restore
- Updating later
- What will probably go wrong
- Out of scope
Read out of the prompt’s own step headings at build time — if the prompt changes, this list changes with it.
You are Claude Code on the user's own computer. There is no server and no Prompt Zero:
everything in this prompt runs on this machine and stays on it.
Run every command on this computer, in the shell you are already in. Nothing in this prompt
uses ssh.
Install IT Tools 2024.10.22-7ca5933 under ~/selfhost/it-tools, answering at
http://localhost:8209.
## 1. Preflight
Why this path still matters: paste a JWT into a random "jwt debugger" website and you handed a
session to someone else's logs. The same tool running at localhost keeps the bytes on this
machine. That is the whole product value. There is still no multi-device sync, no team library and
no launcher integration; Raycast Pro is a different job.
If Docker Desktop was already installed but not running, step 2's `docker info` fails with a
connection error rather than "command not found". Start Docker Desktop and wait until it reports
running, then re-run `docker info` before continuing.
After step 7, bookmark http://localhost:8209/ in the browser profile you actually use for work.
A tool chest you never open is a container burning a little RAM for nothing. If you later want
the same UI on a VPS for phone access, use the server path on this page instead of rebinding the
local port.
Say this to the user before step 2 runs. This is a local utility chest: encoders, converters and
generators that run in the browser against code you host, so secrets need not go to a random
website. There is no account and no sync. The pinned tag `2024.10.22-7ca5933` was published
2024-10-22 (about 21 months before the 2026-08-07 check); release cadence is slow.
Detect the OS and measure the machine:
```bash
uname -s
case "$(uname -s)" in
Darwin) vm_stat | awk '/page size/{p=$8} /free|inactive/{s+=$3} END {printf "%d MB available\n", s*p/1048576}' ;;
Linux) . /etc/os-release && echo "$ID $VERSION_CODENAME"; free -m | awk '/^Mem:/ {print $7 " MB available of " $2 " MB"}' ;;
MINGW*|MSYS*) powershell -Command "(Get-CimInstance Win32_OperatingSystem).FreePhysicalMemory" | awk '$1+0 {printf "%d MB available\n", $1/1024}' ;;
esac
df -h ~
```
IT Tools needs 256 MB of RAM available and 2 GB free on the home disk. The image publishes amd64
and arm64. If available RAM is under 256 MB or free disk is under 2 GB, print both numbers and
stop. Do not install and hope.
## 2. Docker
Check before installing anything:
```bash
docker info >/dev/null 2>&1 && echo "docker OK" || echo "docker MISSING"
docker compose version 2>/dev/null || true
```
If that printed `docker OK` and a compose version, skip to step 3.
Otherwise, install Docker for the OS step 1 detected:
- macOS: if `command -v brew` succeeds, run `brew install --cask docker`. If there is no
Homebrew, STOP: tell the user to download Docker Desktop from
https://www.docker.com/products/docker-desktop/ and install it, and wait until they
confirm. Either way, then STOP: tell the user to open Docker Desktop once, accept its
terms, and wait for the whale icon to say it is running. Do not continue until they
confirm.
- Windows: run `winget install -e --id Docker.DockerDesktop`. If winget is missing or the
install fails, STOP: tell the user to download Docker Desktop from the URL above and
install it, and wait until they confirm. Docker Desktop configures WSL 2 itself and may
ask for a reboot; if it does, STOP and tell the user to reboot and come back, this
prompt resumes at this step. Then STOP: have the user open Docker Desktop, accept its
terms, and confirm it says running.
- Linux, Debian or Ubuntu: install Docker Engine from download.docker.com's apt
repository, with its signing key saved to a file first, never piped into a shell. The
fence is guarded, a no-op on anything but a Linux with apt:
```bash
if [ "$(uname -s)" = "Linux" ] && command -v apt-get >/dev/null 2>&1; then
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/$(. /etc/os-release && echo "$ID")/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/$(. /etc/os-release && echo "$ID") $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo usermod -aG docker "$USER"
fi
```
Adding the user to the docker group is root-equivalent on this machine; say that to the
user in one sentence, and tell them the group change lands at their next login.
- Linux, anything else: STOP. Tell the user to install Docker Engine and the compose
plugin with their distribution's package manager, and to run this prompt again once
`docker info` works.
Assert: `docker info` exits 0 and `docker compose version` prints a version. Do not
continue without both.
## 3. Layout
```bash
mkdir -p ~/selfhost/it-tools/backups
ls -la ~/selfhost/it-tools
```
Assert: `backups` exists. There is no `data/` directory: the app is stateless on the server side.
## 4. Secrets
No secret is generated and there is no `.env` file. IT Tools has no accounts. On this local path
the UI is loopback-only, so the public-by-design risk of the VPS path does not apply the same
way. Still do not invent a login step.
## 5. compose.yml
```bash
cat > ~/selfhost/it-tools/compose.yml <<'EOF'
# IT Tools · the deterministic fallback for the local path. Authored by
# caniselfhostit from the upstream documentation, not copied from a repository:
# docker run ......... https://github.com/CorentinTh/it-tools#self-host
#
# One service on the computer you are sitting at. Stateless static UI. Tag
# 2024.10.22-7ca5933 published 2024-10-22; digest read from Docker Hub on
# 2026-08-07.
#
# NOT YET VERIFIED: no harness run has been recorded against this file.
services:
it-tools:
image: corentinth/it-tools:2024.10.22-7ca5933@sha256:8b8128748339583ca951af03dfe02a9a4d7363f61a216226fc28030731a5a61f
container_name: it-tools
restart: unless-stopped
ports:
# Loopback only: no other device on the wifi can reach 8209.
- "127.0.0.1:8209:80"
EOF
cd ~/selfhost/it-tools && docker compose config >/dev/null && echo "compose OK"
```
Assert: that prints `compose OK`. One service, no volumes, no env file.
## 6. Nothing is public
No reverse proxy, no certificate, no firewall rule. 8209 is bound to 127.0.0.1 only. Confirm:
```bash
grep -c '"127.0.0.1:' ~/selfhost/it-tools/compose.yml
```
Assert: that count is exactly `1`. The user's phone cannot reach it on the wifi unless they
rebind the port, which is out of scope.
## 7. Start and verify
```bash
cd ~/selfhost/it-tools
docker compose pull
docker compose up -d
for i in $(seq 1 24); do code=$(curl -sS -o /dev/null -w '%{http_code}' http://localhost:8209/); echo "$i $code"; [ "$code" = 200 ] && break; sleep 5; done
curl -sSL http://localhost:8209/ | grep -ciE 'it-tools|IT Tools|token|encode|hash|Base64'
docker compose ps
```
Assert: loop ends with `200` and the body grep count is greater than `0`. If the port is already
allocated, find what holds 8209 and stop until it is free. There is no sign-in step.
STOP: tell the user to open http://localhost:8209/, try one tool, and confirm the UI loads
without a login. Do not continue until they confirm.
## 8. First backup and restore
Stateless backup: compose only. No Caddyfile on this path, no data directory.
```bash
cd ~/selfhost/it-tools
tar -C ~/selfhost/it-tools -czf ~/selfhost/it-tools/backups/it-tools-$(date +%F).tar.gz compose.yml
ls -lh ~/selfhost/it-tools/backups/
```
Assert: archive exists and is non-empty. Print its size. Copy it off this computer if you care
about preserving the pin. To restore: untar `compose.yml`, `docker compose up -d`. There is no
user content on disk to lose.
## 9. Updating later
Releases: https://github.com/CorentinTh/it-tools/releases. This pin is from 2024-10-22; check for
a newer stable identity before assuming you are current. Backup compose, edit the image line to
the new tag and digest:
```bash
cd ~/selfhost/it-tools
docker compose pull
docker compose up -d
docker compose logs --tail 20 it-tools
```
Re-run step 7's checks. Do not float to `nightly` from this prompt without an explicit decision.
## 10. What will probably go wrong
You will assume the tools are "always the newest" because the container is healthy. The pin can
be more than a year old and still serve a fine UI. Check releases when a tool misbehaves or when
you care about a fix. Second: rebinding 8209 to all interfaces "so the phone can use it" puts a
password-free utility chest on every network this laptop joins. Keep loopback unless you have a plan.
Third: corporate proxies that break Docker pulls will fail step 7 before the UI ever loads. Fix
the proxy or pull on a network that can reach Docker Hub, then retry. Fourth: an old pin with a
known XSS in a client-side tool is still code that runs in your browser; when a new release
exists, prefer moving the pin over living on nostalgia.
## 11. Out of scope
- Do not expose this to the internet.
- Do not configure port forwarding on the router.
- Do not add a reverse proxy or TLS on this path.
- Do not rebind 8209 to 0.0.0.0.
- Do not invent accounts or a first-run wizard.
- Do not switch the image to `latest` or `nightly` without a deliberate pin decision.compose.local.ymlthe services, pinned · local layout18 lines
# IT Tools · the deterministic fallback for the local path. Authored by
# caniselfhostit from the upstream documentation, not copied from a repository:
# docker run ......... https://github.com/CorentinTh/it-tools#self-host
#
# One service on the computer you are sitting at. Stateless static UI. Tag
# 2024.10.22-7ca5933 published 2024-10-22; digest read from Docker Hub on
# 2026-08-07.
#
# NOT YET VERIFIED: no harness run has been recorded against this file.
services:
it-tools:
image: corentinth/it-tools:2024.10.22-7ca5933@sha256:8b8128748339583ca951af03dfe02a9a4d7363f61a216226fc28030731a5a61f
container_name: it-tools
restart: unless-stopped
ports:
# Loopback only: no other device on the wifi can reach 8209.
- "127.0.0.1:8209:80"agent-readable mirror: /self-host/raycast.md
The files, if you'd rather do it yourself
The cloud path with no agent involved: three files, in the order you'd use them. The cloud prompt above writes exactly these — if the two ever disagree, the files are the ones CI diffs. The local path ships its own compose file, collapsed under its own prompt.
compose.ymlthe services, pinned20 lines
# IT Tools · the deterministic fallback. Authored by caniselfhostit from the
# upstream documentation, not copied from a repository:
# docker run ......... https://github.com/CorentinTh/it-tools#self-host
#
# One service. Static web UI served by the image; no application database, no
# accounts, no env secrets. Upstream publishes docker run lines rather than a
# maintained multi-service compose file (officialCompose: none).
# Tag 2024.10.22-7ca5933 was published 2024-10-22; digest read from Docker Hub
# on 2026-08-07. See block 9 of the prompts for the age of this pin.
#
# NOT YET VERIFIED: no harness run has been recorded against this file.
services:
it-tools:
image: corentinth/it-tools:2024.10.22-7ca5933@sha256:8b8128748339583ca951af03dfe02a9a4d7363f61a216226fc28030731a5a61f
container_name: it-tools
restart: unless-stopped
ports:
# Loopback only: the host's Caddy is the only thing that reaches 8209.
- "127.0.0.1:8209:80"Caddyfilethe hostname and TLS25 lines
# IT Tools · the Caddy site block for this service.
#
# Authored by caniselfhostit from
# https://caddyserver.com/docs/automatic-https
#
# Append this to /etc/caddy/Caddyfile, the Caddy that Prompt Zero installed, with
# <DOMAIN> replaced by the hostname pointed at this box. Everything served here
# is public by design: there are no accounts. Caddy basic_auth is an opt-in if
# you would rather the URL not be world-readable.
<DOMAIN> {
encode zstd gzip
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
Referrer-Policy "no-referrer"
-Server
}
# 8209 is the loopback port compose publishes on this host. It is not a
# container port and it is not open in the firewall.
reverse_proxy 127.0.0.1:8209
}install.shthe same install, no agent114 lines
#!/usr/bin/env bash
# IT Tools · the agent-free install.
#
# Everything prompt.md tells an agent to do, as a script you can read first.
# Run it on the VPS, as a non-root user who is in the docker group:
#
# DOMAIN_HOST=tools.example.com ./install.sh
#
# Authored by caniselfhostit from the upstream documentation:
# https://github.com/CorentinTh/it-tools#self-host
#
# No secret is generated. There are no accounts, no registration form and no
# first-run wizard. The page is public by design: anyone who can reach the
# hostname can use every tool. Put Caddy basic_auth in front if that is not
# what you want. There is no application data directory; backup is compose
# plus the live Caddyfile.
#
# NOT YET VERIFIED: no harness run has been recorded against this script.
set -euo pipefail
APP_DIR="${APP_DIR:-/srv/it-tools}"
DOMAIN_HOST="${DOMAIN_HOST:-}"
die() { printf 'install.sh: %s\n' "$1" >&2; exit 1; }
# --- 1. Refuse to start on a machine that is not ready -----------------------
[ -n "$DOMAIN_HOST" ] || die "set DOMAIN_HOST to the hostname you pointed at this server, e.g. tools.example.com"
command -v docker >/dev/null 2>&1 || die "docker is not installed. Run Prompt Zero first."
docker compose version >/dev/null 2>&1 || die "the docker compose plugin is missing"
command -v caddy >/dev/null 2>&1 || die "caddy is not installed on the host. Run Prompt Zero first."
avail_mb="$(free -m | awk '/^Mem:/ {print $7}')"
[ "$avail_mb" -ge 256 ] || die "only ${avail_mb} MB of RAM available; this install wants 256 MB"
avail_gb="$(df -BG --output=avail /srv | tail -1 | tr -dc '0-9')"
[ "$avail_gb" -ge 2 ] || die "only ${avail_gb} GB free on /srv; this install wants 2 GB"
resolved="$(getent hosts "$DOMAIN_HOST" | awk '{print $1; exit}' || true)"
[ -n "$resolved" ] || die "$DOMAIN_HOST does not resolve yet. Add the A record, wait a minute, run this again."
# --- 2. Lay the files out ----------------------------------------------------
#
# No data/ directory: the container is stateless static UI.
sudo install -d -m 750 -o "$(id -u)" -g "$(id -g)" "$APP_DIR" "$APP_DIR/backups"
install -m 0644 "$(dirname "$0")/compose.yml" "$APP_DIR/compose.yml"
install -m 0644 "$(dirname "$0")/Caddyfile" "$APP_DIR/Caddyfile"
cd "$APP_DIR"
docker compose config >/dev/null
# --- 3. Caddy site block, on the host ----------------------------------------
if ! sudo grep -qF "$DOMAIN_HOST {" /etc/caddy/Caddyfile; then
sudo cp /etc/caddy/Caddyfile "/etc/caddy/Caddyfile.before-it-tools"
printf '\n' | sudo tee -a /etc/caddy/Caddyfile >/dev/null
sed "s|<DOMAIN>|${DOMAIN_HOST}|g" "$APP_DIR/Caddyfile" | sudo tee -a /etc/caddy/Caddyfile >/dev/null
fi
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
# --- 4. Ports ----------------------------------------------------------------
if command -v ufw >/dev/null 2>&1; then
echo "==> 80/tcp and 443/tcp for Caddy, 443/udp for HTTP/3; 8209 stays closed"
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 443/udp
sudo ufw status verbose
fi
# --- 5. Start it -------------------------------------------------------------
docker compose pull
docker compose up -d
echo "==> waiting for https://${DOMAIN_HOST}/"
for _ in $(seq 1 24); do
code="$(curl -sS -o /dev/null -w '%{http_code}' "https://${DOMAIN_HOST}/" || true)"
[ "$code" = "200" ] && break
sleep 5
done
[ "${code:-}" = "200" ] || die "https://${DOMAIN_HOST}/ answered ${code:-nothing}. Check: docker compose logs --tail 40 it-tools"
curl -sSL "https://${DOMAIN_HOST}/" | grep -qiE 'it-tools|IT Tools|token|encode|hash' \
|| die "the page at https://${DOMAIN_HOST}/ does not look like IT Tools"
# --- 6. Backup (compose + live Caddyfile only; no app state) -----------------
STAMP="$(date +%Y%m%d-%H%M%S)"
sudo tar -czf "$APP_DIR/backups/it-tools-${STAMP}.tar.gz" \
-C "$APP_DIR" compose.yml \
-C /etc/caddy Caddyfile
ls -lh "$APP_DIR/backups/"
[ -s "$APP_DIR/backups/it-tools-${STAMP}.tar.gz" ] || die "the backup archive is empty"
cat <<-DONE
IT Tools is answering at https://${DOMAIN_HOST}
1. That page is public. There is no login and no account. Anyone who can
reach the hostname can use every tool. Open it in a private window and
read it as a stranger would. Caddy basic_auth is the opt-in if you want
a password in front.
2. There is no application data directory. The backup is compose.yml plus
the live Caddyfile only.
3. Pinned image tag 2024.10.22-7ca5933 was published 2024-10-22 (about 21
months before this install's 2026-08-07 check). See releases before
assuming a newer stable tag exists.
4. Copy $APP_DIR/backups off this disk if you care about the Caddy site
block; the tools themselves are the image.
5. NOT YET VERIFIED on a clean harness machine.
DONEWhat you're signing up for
The part a vendor's comparison page leaves out. None of it is a reason not to do this; all of it is yours the moment you cancel Raycast.
- This is a utility chest, not a SaaS with accounts and sync. Bookmarks and history stay in the browser.
- The page is public by design: anyone who can reach the hostname can use every tool. Put Caddy basic_auth in front if that is not what you want.
- Trust is the product: you run the code so JWT secrets and private keys are not pasted into a stranger's site. Audit updates like any dependency.
- There is almost no server state to back up. The value is availability of the UI you control; backup is compose plus Caddyfile.
- Upstream release cadence can be slow. The pinned image tag 2024.10.22-7ca5933 was published 2024-10-22 (about 21 months before this page's 2026-08-07 check).
Where this came from
“Useful tools for developer and people working in IT.”
- Upstream publishes Docker images (Docker Hub and ghcr) via docker run examples; there is no multi-service official compose stack. source
- The image is the built single-page app copied into an nginx static server; there is no server-side application to receive tool input, so tools run in the browser. source
- The project is GPL-3.0 licensed. source
- Caddy obtains and renews TLS certificates automatically for any public hostname named in the Caddyfile. source
Questions people actually ask
Answered from this page's own data — the same numbers, in sentences.
Can I self-host Raycast?
Not Raycast itself — the vendor does not ship a version you can run on your own server. What you can self-host is the job people pay it for, and the answer to that is IT Tools. A chest of offline-friendly developer utilities in the browser: encoders, converters, generators and more. The install is one command: one container behind Caddy with automatic TLS, secrets generated on the server rather than in a chat window, and a first backup taken before the agent says it is done, in about 6 minutes. The prompt on this page does it; the compose.yml, Caddyfile and install.sh below do the same install with no agent at all.
What replaces Raycast?
IT Tools. A chest of offline-friendly developer utilities in the browser: encoders, converters, generators and more. A self-hosted chest of encoder/converter/generator utilities so secrets never leave your browser for a random website. It is not a Raycast replacement for window management or extensions; it is the honest swap for the web-tool half of the habit. IT Tools is GPL-3.0-only-licensed and free; nothing on this page is a hosted service we sell you.
What does self-hosting cost compared to Raycast?
256 MB of RAM and 2 GB of disk — the smallest tier most VPS hosts sell, about $5 a month. IT Tools itself is free and GPL-3.0-only-licensed; the bill is the server, plus a domain you probably already own. What you stop paying: Raycast Pro, $8/mo — $96 a year, 1 seat assumed.
How hard is it really?
ONE COMMAND — under 10 minutes. The rule that produced that verdict: one container, no database, no outside integration, at most one secret. Nothing to negotiate with anyone else, nothing to back up separately, at most one secret to generate. This is the case where the compose file honestly is the whole install. The tier is derived from seven countable facts about the IT Tools install, not from anyone's impression of it, and the whole rubric is published on the methodology page.
Can I run IT Tools on my own computer instead of a server?
Yes — that is the second path in the prompt box above. "On my computer" installs the same IT Tools on the machine you are sitting at: no VPS, no domain, no DNS, and nothing exposed to the internet. It checks for Docker first and installs Docker Desktop if the machine does not have it — macOS, Windows and Linux each get their own step — then binds everything to loopback, so the app answers on http://localhost and only on that computer. Worth knowing: Utilities run entirely in the browser against this local copy, which is ideal for secrets you would never paste into a random website. Same discipline as the cloud path: pinned images, secrets generated on the machine, and a first backup taken before the prompt says it is done.
Content last checked 2026-08-07. Verdicts are derived from the published rubric on /methodology; corrections go through the issue tracker.