# SEOnaut: a self hosted crawler that grades a site's SEO issues

> SEOnaut is an open source SEO auditing tool written in Go, with MySQL for storage and an Apache ECharts dashboard, that scans a site and reports broken links, redirect problems, meta tag defects and heading order issues by severity. It ships as a Docker Compose stack so a self hosted instance is a single file.

**StJudeWasHere/seonaut** — Open source SEO audit tool.

- Repository: https://github.com/StJudeWasHere/seonaut
- Website: https://seonaut.org
- Stars: 826 · Forks: 141
- Language: Go
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/stjudewashere-seonaut

## What a scan actually looks for

SEOnaut is an open source SEO auditing tool designed to analyze websites for issues that may impact search engine rankings. It performs a comprehensive site scan and generates a report detailing any identified issues, organized by severity and potential impact on SEO.

The README names the categories it detects, and they are all technical rather than editorial. Broken links, so that a crawl does not leave 404 errors behind. Redirect issues, covering temporary redirects, permanent redirects and redirect loops. Missing or duplicate meta tags. Incorrectly ordered headings. And more, which is where the README stops being specific and the interesting part of the work lives in the code.

That framing is the point of the tool. An SEO tool that reports keyword difficulty is a research tool; one that reports redirect loops and duplicate titles is a crawling and auditing tool, and it fits next to a continuous integration job or a scheduled report rather than in a content planning meeting.

The audience is the technical SEO and web operations side of a team, and the operator who has to run the thing. A hosted version is available at seonaut.org, which gives you a way to look at the interface before you decide to run your own.

## Three severity levels and what they mean for the report

SEOnaut categorizes issues into three levels of severity: critical, high and low. Three levels is a deliberately coarse model, and it has consequences worth thinking about before you adopt it.

On the positive side, a small fixed set means the report is comparable across sites and across time. A scan of a large site will produce many findings, and a tri-state classification gives you a triage order rather than a long list of equal severity items. Critical findings are the ones that break the site, high findings are the ones that cost ranking, and low findings are the ones to batch up.

On the negative side, there is no numeric score in the README's description, no weighting and no domain specific thresholds. Whether a duplicate meta tag on a paginated listing is high or low is decided by the implementation rather than by something you can configure, and the README does not document a way to change the thresholds.

So the honest way to read a SEOnaut report is as a defect list with a priority ordering, not as a ranking score. If you need to show a trend, such as a count of critical findings per release, the database behind it makes that easy, because the results are stored rather than rendered and thrown away. That is the part of the design that matters for automation: SEOnaut is a system that keeps its output, and the dashboard is one view of it.

## A Go service, MySQL storage and an ECharts dashboard

SEOnaut is a web-based application built with the Go programming language and a MySQL database for data storage. The frontend is designed for simplicity, using custom CSS and minimal vanilla JavaScript, and Apache ECharts provides the interactive dashboard experience.

That stack choice is what makes the tool deployable. A single Go binary with a MySQL backend and a static asset bundle is a service you can put behind a reverse proxy without thinking about a runtime, a package manager or a build step for the front end. The dependency list in go.mod reads like a crawler, with htmlquery and bluemonday for parsing and sanitising HTML, gopher-parse-sitemap and go-sitemap for sitemap handling, robotstxt for robots.txt, warc for web archive files, and a user agent library to identify itself.

The rest of the module list is application plumbing: the MySQL driver, golang-migrate for schema migrations, viper for configuration, the gorilla packages for sessions, secure cookies and websockets, uuid for identifiers, monday for the dashboard data shape, and the standard yaml library for configuration files. The repository layout matches, with cmd/ for the entry point, internal/ for the application code, migrations/ for the schema, translations/ for localisation, web/ for the front end assets, plus config/, archive/ and docs/.

It is possible to configure a custom database and compile SEOnaut manually, but the README recommends using the provided Docker files, which it says simplify the setup and remove the need for manual configuration.

## Running the stack with docker compose

Because a database is required, the documented deployment is a Compose file with two services. The application service in the README example pulls the published image and publishes the port:

```yaml
  app:
    image: ghcr.io/stjudewashere/seonaut:latest
    container_name: "SEOnaut-app"
    ports:
      - "${SEONAUT_PORT:-9000}:9000"
    depends_on:
      - db
    command: sh -c "/bin/wait && /app/seonaut"
```

The db service is MySQL 8.4 with its own container name, and the environment block sets MYSQL_ROOT_PASSWORD, MYSQL_DATABASE, MYSQL_USER and MYSQL_PASSWORD, all set to simple defaults in the example. The two services sit on a bridge network called seonaut_network.

Two pieces of the app service deserve explanation. The command runs the wait binary before the server, and the WAIT_HOSTS, WAIT_TIMEOUT, WAIT_SLEEP_INTERVAL and WAIT_HOST_CONNECT_TIMEOUT variables configure how that wait behaves against db:3306. The commented block underneath lists the SEONAUT prefixed overrides for the server host, port, public URL and the database server, port, user, password and database name, each with a default, so the defaults can be replaced with environment variables without editing the file.

The security note is in the README too, and it should be read before any deployment beyond a laptop. SEOnaut is set up to run on port 9000 using unencrypted HTTP by default, and the README recommends configuring HTTPS with a reverse proxy so that communication between client and server is encrypted.

## Building from source and the multi-stage image

The from source route in the README is five steps: install Docker, clone the repository with git clone https://github.com/stjudewashere/seonaut.git, change into the project directory with cd seonaut, then build and start the containers and open the browser:

```bash
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build
```

The application is then reachable at http://localhost:9000. The detailed installation and configuration instructions live in docs/INSTALL.md.

The Dockerfile is a three stage build and each stage has a clear job. The builder compiles the Go server with the race detector off and a static binary:

```dockerfile
FROM golang:1.25-alpine3.22 AS builder
```

The second stage is a Node 18 Alpine image that installs esbuild and bundles the stylesheet into static assets with the fonts loaded as files. The final stage is a plain Alpine image that copies in the compiled binary, the front end bundle, the translations directory, the migrations and the config directory.

The last piece is the wait binary, which is fetched from the docker-compose-wait releases at build time with the architecture substituted so that the right build is pulled for amd64, arm64 or arm v7 hosts, pinned to version 2.12.1 through a build argument.

For local development without Docker, the Makefile has the equivalents: make run starts the server with the race detector and a local configuration, make test runs the race enabled test suite over the internal packages, make vet runs go vet, and the front and watch targets run esbuild to bundle the stylesheet.

## Where SEOnaut is the wrong tool

Three cases point elsewhere.

The first is a need for content or market analysis. SEOnaut audits technical SEO on a site it can crawl. It says nothing about which terms to target, how competitors rank, or whether the copy on the page is any good. A team that wants those answers needs a different tool, and a crawler will not become one.

The second is a site that cannot be crawled from the public internet. SEOnaut runs as a web service that fetches pages, so a staging environment behind a VPN or an intranet application is out of reach unless you point the tool at it from inside the network, and the audit then reflects whatever that network can see.

The third is a scale question. A comprehensive site scan of a large site produces a lot of rows, and the README gives no guidance on crawl limits, scheduling or politeness, and no configuration knobs for them in the documented environment variables. Before running a scan against a site you care about, the README's own instruction to read INSTALL.md is the place to look for those settings.

One more practical point about the deployment. The example Compose file ships with the database password set to seonaut and the root password set to root, and the published image is tagged latest rather than a version. For anything beyond a trial, pin a version of the image and change the credentials, because a scanner that can log into its own database is not a security property anyone wants.

## Conclusion

Adopt SEOnaut if you need repeatable, self hosted technical SEO audits across multiple sites and want the findings in a database you own rather than in someone else's dashboard. Do not adopt it if you need keyword research, rank tracking or content analysis, since the tool's scope is a technical site scan, and do not expose it to the internet without a reverse proxy in front of it. Verify three things first: that the MySQL credentials in the Compose file are changed before any deployment beyond a laptop, since the defaults are written into the example; how the tool handles robots.txt and sitemaps in your environment, because it depends on libraries for both; and whether your reporting needs match the critical, high and low severity model, since that classification is the shape of every result the tool produces. The licence is MIT, the last push was on 2026-10-08, and the repository publishes no GitHub releases.

## FAQ

### What issues does SEOnaut detect during an audit?

The README lists broken links so that 404 errors are avoided, redirect issues including temporary redirects, permanent redirects and loops, missing or duplicate meta tags, and incorrectly ordered headings, along with other problems found during a comprehensive site scan.

### How are SEOnaut findings prioritised?

Issues are categorised into three levels of severity: critical, high and low. The report is organised by severity and by potential impact on SEO, so the findings read as a triage order rather than a flat list.

### How do I run SEOnaut with Docker?

Create a docker-compose.yml with a MySQL 8.4 service and an app service using the ghcr.io/stjudewashere/seonaut:latest image with the port mapping ${SEONAUT_PORT:-9000}:9000 and the command that runs the wait binary before the server. The README's example is given in full and the defaults can be overridden with environment variables.

### Does SEOnaut serve its interface over HTTPS?

Not by default. The README states that SEOnaut is set up to run on port 9000 using unencrypted HTTP by default, and recommends configuring HTTPS using a reverse proxy for encrypted communication between the client and the server.

### What is SEOnaut built with?

A Go web application with a MySQL database for storage, a simple frontend using custom CSS and minimal vanilla JavaScript, and Apache ECharts for the interactive dashboard. The crawler side depends on libraries for HTML parsing, sitemaps, robots.txt and WARC files.

## Sources

- [Issues](https://github.com/StJudeWasHere/seonaut/issues)
- [License: MIT](https://github.com/StJudeWasHere/seonaut/blob/main/LICENSE)
- [Project website](https://seonaut.org)
- [README](https://github.com/StJudeWasHere/seonaut/blob/main/README.md)
- [StJudeWasHere/seonaut on GitHub](https://github.com/StJudeWasHere/seonaut)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/stjudewashere-seonaut
