Docker Compose stack providing legacy web access for the iMac G3.
- Macproxy — primary choice on Tiger/Aquafox; fast, plain HTML, upstream certificate validation.
- WebOne — fallback when you want more layout/images; richer but slower.
- Crypto Ancienne (
carl) — only for OS 9/Classilla special cases; no certificate validation. - Browservice — server-side Chromium rendering; streams pages as JPEG frames so the iMac needs no JavaScript, TLS, or CSS support. Validated on Tiger (G3). Use it for modern sites that break with the other proxies. OS 9/Classilla not yet tested; PowerPC/m68k support is not tested upstream.
- AdGuard Home — standalone DNS blocker; optional for the G3 and independent of the browser proxy choice.
No sensitive logins or banking through this machine. Macproxy/WebOne validate upstream
certificates, but between the iMac and the NAS the traffic is local proxy traffic. carl
is opportunistic compatibility, not a secure TLS solution.
Finding on the G3: pages load faster with Macproxy than with WebOne. Hence this order.
Macproxy, WebOne, and carl are HTTP proxies — you configure them once in the OS or browser proxy settings and then browse normally with the browser's own address bar. The proxy works invisibly in the background.
Browservice is different — do not configure it as a proxy. Instead, surf directly to
http://<NAS-IP>:8083/ and type the destination URL inside the Browservice interface. You are
then looking at a live Chromium browser running on the server, streamed as images to the iMac.
Use it as a last resort when Macproxy and WebOne cannot handle a site.
The table below combines the recommended use, the port, and how to point the browser at it:
| Use | Service | Port | How to point the browser |
|---|---|---|---|
| Daily browsing (Safari) | Macproxy | 5003 |
HTTP proxy in the OS X network settings |
| More layout/images (Aquafox) | WebOne | 8091 |
HTTP proxy in Aquafox's own settings |
| Modern sites, last resort | Browservice | 8083 |
Open http://<NAS-IP>:8083/ directly; use its in-page address bar |
| OS 9 / Classilla | carl |
8767 |
HTTP proxy; no certificate validation; not for Tiger |
| DNS-wide ad/tracker filtering | AdGuard Home | — | Set as the DNS server in the network settings |
Current deployment target: Synology DS920+ running Docker / Portainer. Other Linux Docker hosts should work, but may need adjustments for ports, capabilities, and persistent volume paths.
Platform requirement: x86_64 only. The Browservice AppImage is x86_64-only and will not run on ARM-based NAS devices (DS220j, DS420j, …) or Apple Silicon. All other services (Macproxy, WebOne, carl, AdGuard Home) are multi-arch and run on ARM without changes.
| Service | Current host port | Internal |
|---|---|---|
| Macproxy | 5003 |
5001 |
| WebOne | 8091 |
8080 |
| carl | 8767 |
8765 |
| Browservice | 8083 |
8080 |
| AdGuard Home DNS | 5354 (set 53 in .env for G3 use) |
53 |
| AdGuard Home setup | 3080 |
3000 |
| AdGuard Home admin | 3001 |
80 |
Host ports are configurable via .env.
- Docker with Compose v2 — use
docker compose(plugin), not the legacydocker-composewrapper. - Linux x86_64 host — Browservice fetches an x86_64 AppImage at build time and will not run on ARM. Other services have no architecture restriction.
- Copy
.env.exampleto.envand set at leastPROXY_HOSTNAMEto the IP or hostname of the machine running this stack. - Start the stack:
docker compose up -d --build
carlandbrowserviceare compiled from source on first start; allow a few minutes for the build. - Configure the browser's proxy to the host IP and the relevant port (see Ports).
Browservice is not a proxy — open
http://<NAS-IP>:8083/directly in the browser instead. - Optional: use AdGuard Home DNS for G3-wide DNS blocking.
Portainer CE cannot use the local build: flow for cryanc/carl or browservice,
so the Portainer stack uses the GitHub Actions images instead:
ghcr.io/tommiec/cryanc-carl:latest
ghcr.io/tommiec/browservice:latest
See deploy/ for a pre-built-images stack and .env template. The real
deployment journal can still live in a separate GitOps repository.
To publish your own images after forking:
- Push any change to
cryanc/orbrowservice/— GitHub Actions builds and publishesghcr.io/<your-username>/cryanc-carl:latestandghcr.io/<your-username>/browservice:latest. - Go to your GitHub profile → Packages, open each package, and set it to Public. GHCR packages default to private; the NAS cannot pull them without authentication.
- Update the two image names in
deploy/compose.yamlto match your username.
This section gives the step-by-step proxy settings for Macproxy and WebOne; see Choice for which service to use when. Browservice is not a proxy and is covered separately in Browservice.
Do not browse to the proxy URL itself. WebOne will show a "looped connection" page if you do — that just means it is reachable.
Replace <NAS-IP> in the steps below with the IP address or hostname of the machine
running this stack (e.g. 192.168.1.10).
- Open Aquafox → Preferences → Advanced → Network → Settings.
- Choose Manual proxy configuration.
- Set HTTP Proxy to
<NAS-IP>, port5003. - Set SSL Proxy also to
<NAS-IP>, port5003, or use the option "Use this proxy server for all protocols" if it is visible in your build. - Under No Proxy for, set at least
localhost, 127.0.0.1, <NAS-IP>. - Test with
http://frogfind.com/orhttps://example.com/.
Use WebOne as a fallback by temporarily setting the same fields to port 8091. Do not
use carl in Aquafox.
Safari uses the OS X network settings:
- System Preferences → Network → active interface (Ethernet) → Proxies tab.
- Check Web Proxy (HTTP) and set server
<NAS-IP>, port5003for Macproxy, or8091for WebOne. - Check Secure Web Proxy (HTTPS) too with the same server and port.
- Under bypass/exceptions, set at least
<NAS-IP>. - Save/apply and surf to a normal site, not to the proxy itself.
For Safari use the same port choice: Macproxy 5003, WebOne 8091. Do not use carl
for Safari/Aquafox.
Browservice runs a full Chromium instance on the server and streams the rendered page as JPEG frames over HTTP to the client browser. The iMac sends only mouse/keyboard events and receives images — no JavaScript execution, no TLS handshake, no modern CSS parsing happens on the G3 side.
Use it when Macproxy or WebOne cannot render a page you actually need. Validated on Tiger (G3, Safari/Aquafox). OS 9/Classilla not yet tested.
Do not add Browservice to the proxy settings. Open http://<NAS-IP>:8083/ directly
in Safari or Aquafox as a regular URL. Browservice presents its own start page with an
address bar; type the destination URL there. All rendering happens on the server — Safari
only receives images and sends back mouse/keyboard events.
Browservice fetches the latest AppImage from the official GitHub Releases at Docker build time. To update:
- Edit
browservice/Dockerfile(bump a comment or the base image) and push tomain. - GitHub Actions rebuilds and publishes
ghcr.io/tommiec/browservice:latest. - Watchtower pulls the new image and restarts the container.
Browservice uses Chromium/CEF. The chrome-sandbox binary is set to root-owned mode
4755 in the Dockerfile so the SUID sandbox can run as the non-root browservice
user. The compose file grants SYS_ADMIN and sets seccomp=unconfined because
Synology DSM disables unprivileged user namespaces and Docker's default seccomp
profile blocks some Chromium syscalls. Do not add no-new-privileges:true to
Browservice; it blocks the setuid bit. Browservice keeps its runtime home inside
the container; no host bind mount is needed unless we later decide to preserve
browser sessions, cache, or installed fonts across container replacement. Do not
run this proxy on an internet-exposed host without additional network-level
protection.
GPU hardware acceleration is deliberately disabled (--disable-gpu,
--disable-features=Vulkan). The DS920+ has an Intel UHD 600 that could be
passed through via devices: [/dev/dri:/dev/dri], but the J4125 CPU handles
software rendering fine for a single-user proxy, and sharing the GPU with DSM's
own transcoding services (Plex, Video Station) introduces contention. If
rendering becomes a bottleneck with heavier use, GPU passthrough is a viable
next step: add the device, set the correct video group GID in the Dockerfile,
and remove the disable flags.
AdGuard Home is included as a standalone DNS service. It is not wired into the proxy containers: proxy choice stays in the browser, DNS choice stays in OS X Network settings.
To use it from the G3:
- Set
ADGUARD_DNS_PORT=53in.envbefore starting the stack. Standard DNS clients use port53; the default5354is only a collision-safe startup value. - Start the stack and finish the setup wizard at
http://<NAS-IP>:3080/. - In the wizard, keep DNS listening on internal port
53. Use internal port80for the admin UI if you want to reach it athttp://<NAS-IP>:3001/after setup. - On Tiger: System Preferences → Network → active interface → DNS tab. Add
<NAS-IP>as the DNS server.
This filters DNS for all G3 traffic. It does not change which proxy the browser uses.
Treat AdGuard Home as a light DNS filter for the G3, not as an aggressive adblock setup. The goal is fewer tracker, ad, metrics, and pixel requests while keeping old sites usable.
In the setup wizard:
- Keep DNS listening on internal port
53. - Keep the admin UI on internal port
80so it remains reachable athttp://<NAS-IP>:3001/. - Use your normal LAN resolver or router as upstream DNS. For a NAS in a server VLAN behind a router or firewall, this is usually the gateway or resolver for that server network.
- Use plain DNS upstreams first. DoH/DoT is not needed for this use case.
In Filters -> DNS blocklists, start small:
AdGuard DNS filter
https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt
HaGeZi Multi LIGHT
https://raw.githubusercontent.com/hagezi/dns-blocklists/main/adblock/light.txt
If that is stable but still lets too much tracking through, replace HaGeZi LIGHT with:
HaGeZi Multi NORMAL
https://raw.githubusercontent.com/hagezi/dns-blocklists/main/adblock/normal.txt
Avoid stacking aggressive Pro, Ultimate, gambling, social, annoyance, or cookie lists at first. They are more likely to break old browsing than to help the G3.
Recommended DNS settings:
- Protection: on.
- Blocking mode:
NXDOMAIN. - Blocked response TTL:
3600. - Cache size:
64 MBor higher. - Optimistic cache: on, if available.
- Minimum TTL:
300. - Maximum TTL:
86400. - Safe Search, parental control, and browsing security: off for the first test round.
Only add custom filtering rules when the query log shows they are still needed:
||googletagmanager.com^
||google-analytics.com^
||doubleclick.net^
||googlesyndication.com^
||facebook.net^
||connect.facebook.net^
||scorecardresearch.com^
||hotjar.com^
||newrelic.com^
||sentry.io^
||segment.io^
Do not broadly block shared infrastructure such as gstatic.com, Cloudflare, Akamai,
Fastly, or common JavaScript CDNs. That usually breaks more pages than it speeds up.
Final check: open Query Log in AdGuard Home, filter on the G3 IP, and browse with Aquafox/Macproxy. If you see little or no traffic from the G3, the browser path is probably resolving DNS inside the proxy container instead. In that case AdGuard still filters direct G3 traffic, but proxy-side DNS filtering needs a separate decision.
References:
- https://github.com/AdguardTeam/AdGuardHome/wiki
- https://github.com/AdguardTeam/AdGuardHome/wiki/Configuration
- https://github.com/hagezi/dns-blocklists
Not part of the current stack, but documented for later evaluation.
- retro-proxy (https://github.com/DrKylstein/retro-proxy) — an HTTPS→HTTP
transcoding proxy in Node.js, with conversion/compression options and an
allowed.txtto skip transcoding per site. Functionally in the same category as Macproxy/WebOne, but a Node/Yarn build rather than a ready-made Docker image, so it would need its own container and a G3 test before adoption. BSD-3-Clause.
Carl is not a standard HTTP CONNECT proxy. It uses a socat/inetd model:
each incoming TCP connection forks a carl process that reads a raw HTTP
request from stdin and writes the response to stdout. Carl handles the TLS
negotiation to the upstream server on behalf of the client.
This means:
- Classilla / OS 9: sends a full
GET http://example.com/ HTTP/1.0to the proxy — exactly what carl expects. TLS is transparent to the browser. - Modern browsers (Safari, Firefox, curl with HTTPS): send
CONNECT example.com:443 HTTP/1.1— carl does not understand CONNECT and exits with status 1. Socat logschild exited with status 1. This is expected.
Check the port is open:
nc -z <NAS-IP> 8767 && echo "open"Test plain HTTP (not HTTPS) through carl:
curl --proxy http://<NAS-IP>:8767 http://neverssl.com/HTTPS through carl cannot be tested from a modern client — that requires a browser like Classilla that does not use CONNECT.
- Use
carlonly for OS 9/Classilla; modern browsers must use Macproxy or WebOne. - Do not bind these proxies unprotected to the internet; they have no authentication. Keep them on your LAN.
This repository defines how the stack is built and which images it uses. It does
not run an updater by itself. In production, a deployment layer such as Watchtower,
Portainer, a scheduled docker compose pull, or another GitOps runner decides when
to pull newer images and restart containers.
| Component | Image source | Update source | Notes |
|---|---|---|---|
| AdGuard Home | adguard/adguardhome:latest |
Upstream image registry | App/config data persists in the AdGuard volume. Review release notes before major upgrades. |
| WebOne | u306060/webone:latest |
Upstream Docker image | Image packaging follows the WebOne Docker image maintainer; config format can change between versions. |
| Macproxy Classic | rdmark/macproxy:latest |
Upstream Docker image | Pull the upstream image to update. |
cryanc/carl |
ghcr.io/tommiec/cryanc-carl:latest in deploy mode; local build in root compose |
This repository's GitHub Actions | Rebuilt only when cryanc/** or its workflow changes. |
| Browservice | ghcr.io/tommiec/browservice:latest in deploy mode; local build in root compose |
This repository's GitHub Actions + upstream Browservice AppImage fetched at build time | Rebuilt only when browservice/** or its workflow changes. A normal container pull does not fetch a newer AppImage unless this repo has rebuilt the image first. |
With plain Docker Compose, update registry-backed images with:
docker compose pull
docker compose up -dFor the root development compose, cryanc/carl and Browservice are local builds. Use
docker compose up -d --build after changing their Dockerfiles.
For the pre-built deploy compose, cryanc/carl and Browservice update only after
GitHub Actions publishes new GHCR images:
cryanc/carl:
- Edit
cryanc/Dockerfile(or bumpCRYANC_REFto pin a specific upstream commit/tag). - Push to
main— GitHub Actions rebuilds and publishes the image. - Pull the new image and restart:
docker compose pull cryanc && docker compose up -d cryanc. - Test at least
https://example.com/and the OS 9/Classilla casecarlis meant for. Confirm working only after a short G3 test, not just because the container starts.
Browservice:
- Edit
browservice/Dockerfile(or bump a comment/base image to force a rebuild). - Push to
main— GitHub Actions fetches the current upstream AppImage and publishesghcr.io/tommiec/browservice:latest. - Pull the new image and restart:
docker compose pull browservice && docker compose up -d browservice. - Test the Browservice start page and at least one simple HTTPS site before calling it healthy.
The cryanc container runs as a non-root user, with no-new-privileges, without extra
Linux capabilities, and with a simple TCP healthcheck on the internal CARL_PORT.
This repository is a Docker Compose wrapper. All the actual proxy, browser, and DNS work is done by upstream projects — credits and thanks go to their authors and contributors:
| Project | Author / maintainer | Repository / image | License note |
|---|---|---|---|
| Macproxy Classic | rdmark | https://github.com/rdmark/macproxy_classic | BSD-3-Clause |
| WebOne | WebOne Development Team | https://github.com/atauenis/webone | WebOne custom permissive license; notify users about traffic changes/security implications |
| WebOne Docker image | u306060 / way5 | u306060/webone, https://github.com/way5/docker-webone |
Docker image packaging for WebOne |
Crypto Ancienne (carl) |
Cameron Kaiser | https://github.com/classilla/cryanc | Upstream repository does not publish standard license metadata; review upstream terms before redistributing modified builds |
| Browservice | Topi Talvitie | https://github.com/ttalvitie/browservice | MIT |
| AdGuard Home | AdGuard Team | https://github.com/AdguardTeam/AdGuardHome | GPL-3.0 |
| HaGeZi DNS blocklists | hagezi | https://github.com/hagezi/dns-blocklists | GPL-3.0 |
The MIT license in this repository applies to this wrapper's compose files, Dockerfiles, and documentation. It does not relicense upstream software or container images.
AI was used as a sounding board for Docker, Compose and troubleshooting decisions. The architecture, implementation, validation and maintenance are mine.
