Self-hosting mail the hard way (Postfix + Dovecot + PostfixAdmin + Roundcube, and zero Mailcow)

If you came here scared of mail servers: good. That means you’re paying attention. This post is for you. I assume you can SSH into a Linux box and copy-paste carefully. I do not assume you already know what an MX record is, why port 587 exists, or why Cloudflare’s orange cloud and SMTP should never meet at a party. I wanted real email on my own machine — mail.alipourimjourneys.ir — without buying a “smart” appliance and without installing one of those giant all-in-one stacks. Mailcow is excellent. Stalwart is excellent. I still said no. Sometimes the point is to feel each moving part with your own hands, even if that means spending an evening arguing with nginx about whether a certificate belongs to mail or to Matrix. (Spoiler: when in doubt on this box, it was Matrix.) ...

July 24, 2026 · 17 min · Iman Alipour

A TU Wien CTF where Sysops Klaus left the keys in the cron job

LFI on a meme gallery, a localhost-only password reset service, a world-writable cron script, and way too many rabbit holes — plus a bonus on the intended log poisoning path I griefed myself out of.

June 5, 2026 · 10 min · Iman Alipour

Nextcloud on a Raspberry Pi, Fronted by a Tiny VPS — Nginx Reverse Proxy, Trusted Proxies, and Basic Auth for Jellyfin

I didn’t “move Nextcloud to the cloud”. I moved the front door to a VPS… and kept the house on my Raspberry Pi. 😄 This post documents the setup I ended up with: A public VPS (host: funbox) running Nginx + Let’s Encrypt A private Raspberry Pi (host: iot-hub) running Docker services (Nextcloud, Jellyfin, …) A private backhaul using Tailscale (the 100.x.y.z network) A correct Nextcloud reverse-proxy configuration (trusted_domains, trusted_proxies, and overwrite values) A pragmatic security layer for media: Basic Auth at Nginx for Jellyfin (in addition to Jellyfin’s own login) I’m writing this as a “future me” note and a “copy-paste friendly” guide. ...

February 2, 2026 · 6 min · Iman Alipour

Running my blog on Tor (.onion) and the Clearnet with Hugo + PaperMod

TL;DR — The site is built once (Hugo project), published twice: Onion: http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ via Tor, no TLS/HSTS, bound to 127.0.0.1:3301. Clearnet: https://blog.alipourimjourneys.ir behind Cloudflare, Let’s Encrypt cert, Onion-Location header pointing to the onion mirror. 1) Tor hidden service (onion) basics I used Tor’s v3 onion services and mapped onion port 80 → my local web server on 127.0.0.1:3301. Install & configure Tor (Debian/Ubuntu): sudo apt update && sudo apt install -y tor sudoedit /etc/tor/torrc Add: ...

September 19, 2025 · 6 min · Iman Alipour
HedgeDoc collaborative editing

Self-Hosting HedgeDoc with Docker + Nginx + Let's Encrypt

HedgeDoc is an open-source collaborative Markdown editor. Think Google Docs for Markdown: multiple people can edit the same note in real-time, with support for diagrams, math, polls, and slide decks. In this post we’ll walk through setting up your own instance on a server, secured with HTTPS. Prerequisites A Linux server with Docker and Docker Compose A domain name pointing to your server (e.g. notes.alipourimjourneys.ir) Nginx installed for reverse proxying Certbot for Let’s Encrypt certificates 1. Create the project directory mkdir ~/hedgedoc && cd ~/hedgedoc 2. Create .env POSTGRES_PASSWORD=ChangeThisStrongPassword HD_DOMAIN=notes.alipourimjourneys.ir Generate a strong password with: ...

August 29, 2025 · 2 min · Iman Alipour
Matrix, Signal but distributed

Self-hosting Matrix + Element Call with LiveKit: from zero to working (and the [not so!!] fun debugging along the way)

TL;DR: The call kept saying “waiting for media” because the browser never opened a WebSocket to LiveKit. The root cause was duplicate Access-Control-Allow-Origin headers on /sfu/get (CORS), which stopped the JWT response. Fixing CORS and ensuring the WS proxy worked (HTTP 101 in logs) solved it. What I’m building A Matrix homeserver (Synapse) at matrix.example.com (replace with your domain). TURN/STUN (coTURN) for NAT traversal. Element Call backed by LiveKit, fronted by rtc.example.com. Nginx (host) as the single reverse proxy for everything. Cloudflare DNS (with rtc.* set to DNS-only, no orange cloud). UFW firewall opened for Matrix federation, TURN, and LiveKit media ports. I used Docker for Synapse, PostgreSQL, LiveKit and the JWT helper. I used host Nginx (not Nginx in Docker) to avoid port binding conflicts on 80/443/8448. ...

August 26, 2025 · 8 min · Iman Alipour
RSS Visitors this week: