# cesanta/mongoose: a two-file network stack for microcontrollers

> Mongoose ships HTTP, WebSocket, MQTT and TLS in one C file plus one header, so a device can serve a dashboard or publish telemetry without an operating system underneath. The trade-off is the licence and the amount of networking you have to write yourself.

**cesanta/mongoose** — Embedded web server, with TCP/IP network stack, MQTT and Websocket

- Repository: https://github.com/cesanta/mongoose
- Website: https://mongoose.ws
- Stars: 13,065 · Forks: 2,942
- Language: C
- License: NOASSERTION
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/cesanta-mongoose

## The problem Mongoose solves on a device with no operating system

A microcontroller with an Ethernet PHY has no sockets, no DNS, no HTTP parser and no TLS. The usual answer is to add an RTOS, then lwIP, then a TLS library, then an HTTP library on top, and to keep all four in step across every board you ship. Mongoose collapses that into two files. The README states the pitch directly: drop mongoose.c and mongoose.h into any C or C++ project and get HTTP, WebSocket, MQTT, TLS and firmware OTA updates. The audience is firmware engineers building IoT devices, device dashboards, REST endpoints on microcontrollers and MQTT telemetry links. It is not aimed at application developers who want a framework with routing decorators and an ORM.

## How the event manager, listeners and protocol handlers fit together

Everything runs through struct mg_mgr, an event manager that owns a set of connections. You initialise it with mg_mgr_init, register listeners such as mg_http_listen or mg_mqtt_connect, and then call mg_mgr_poll in a loop. Each connection carries an event handler function with the signature void (*)(struct mg_connection *, int ev, void *ev_data). The ev argument is the event type, and ev_data points at a protocol-specific structure: struct mg_http_message for MG_EV_HTTP_MSG, struct mg_mqtt_message for MG_EV_MQTT_MSG. That single dispatch shape covers HTTP, WebSocket, MQTT and raw TCP, which is why the same code style appears in every example. The polling model means there is no background thread and no hidden allocation strategy you did not choose. For networking, Mongoose either uses the BSD socket API of an existing stack (lwIP, Zephyr, Amazon FreeRTOS-TCP, or Linux, macOS and Windows) or its own built-in TCP/IP stack for bare metal and RTOS targets. The README lists the built-in stack as covering STM32, NXP, Microchip ATSAME54, Renesas RA5M/RA6M/RA8M, Infineon XMC4/XMC7, TI TM4C and TMS570, Cypress WiFi chips, Wiznet W5500 and W5100, and cellular parts including NRF9160 and SIM800.

## Installing Mongoose and serving a directory over HTTP

There is no package manager step. The README's integration story is copying two files into your tree and compiling them alongside your own source, and the comment in the first example spells out the command: build with cc main.c mongoose.c. The snippet below is the README's directory server. Save it as main.c, place mongoose.c and mongoose.h beside it, and create a web_root directory with an index.html inside. After building and running, an HTTP client pointed at port 8000 gets whatever is in web_root.

```c
#include "mongoose.h"   // To build, run: cc main.c mongoose.c

void ev_handler(struct mg_connection *c, int ev, void *ev_data) {
  if (ev == MG_EV_HTTP_MSG) {
    struct mg_http_message *hm = (struct mg_http_message *) ev_data;
    struct mg_http_serve_opts opts = { .root_dir = "./web_root/" };
    mg_http_serve_dir(c, hm, &opts);
  }
}

int main(void) {
  struct mg_mgr mgr;
  mg_mgr_init(&mgr);
  mg_http_listen(&mgr, "http://0.0.0.0:8000", ev_handler, NULL);
  for (;;) {
    mg_mgr_poll(&mgr, 1000);
  }
  return 0;
}
```

The second README example replaces the static file handler with a JSON endpoint. It matches the URI with mg_match and replies with mg_http_reply, using the MG_ESC macro to emit a properly quoted JSON key. Requests to any other URI fall through to a 500 with an error field.

```c
static void ev_handler(struct mg_connection *c, int ev, void *ev_data) {
  if (ev == MG_EV_HTTP_MSG) {
    struct mg_http_message *hm = (struct mg_http_message *) ev_data;
    if (mg_match(hm->uri, mg_str("/api/time/get"), NULL)) {
      mg_http_reply(c, 200, "", "{%m:%lu}\n", MG_ESC("time"), time(NULL));
    } else {
      mg_http_reply(c, 500, "", "{%m:%m}\n", MG_ESC("error"), MG_ESC("Unsupported URI"));
    }
  }
}
```

For MQTT the README connects to a public broker and subscribes inside the MG_EV_MQTT_OPEN branch, then publishes from MG_EV_MQTT_MSG. Note the reconnect timer: a separate function checks whether the stored connection pointer is NULL and reconnects. That pattern is not optional decoration. If you skip it, a dropped broker connection leaves the device silent.

```c
static const char *s_mqtt_url = "mqtt://broker.hivemq.com:1883";
static struct mg_connection *s_mqtt_conn = NULL;

static void timer_fn(void *arg) {
  struct mg_mgr *mgr = (struct mg_mgr *) arg;
  if (s_mqtt_conn == NULL) {
    struct mg_mqtt_opts opts = {.clean = true};
    s_mqtt_conn = mg_mqtt_connect(mgr, s_mqtt_url, &opts, ev_handler, NULL);
  }
}
```

## Where the two-file promise stops being true

Two files is the integration surface, not the whole cost. The README says Mongoose can use external TLS libraries such as mbedTLS or OpenSSL, and it also ships its own TLS 1.3 ECC stack. Choosing between them is a real decision the README does not resolve for you: the built-in stack keeps the dependency count at zero but limits you to the cipher suites and certificate handling that stack implements, while mbedTLS or OpenSSL gives you a wider ecosystem at the price of a second library to build, size and patch. The same ambiguity applies to networking. If your chip is not in the built-in TCP/IP stack table, you are running on top of lwIP or Zephyr after all, and the claim that Mongoose does not depend on other software to implement networking no longer applies to your build. Firmware OTA is listed as built-in for STM32 H5, STM32 H7, NXP IMXRT, RP2040/2350 and ESP32, which means it is not built-in for everything else. And a polling loop with a 1000 millisecond timeout is a design commitment: in a bare-metal target, that loop is your main loop, and anything that blocks inside an event handler stalls every other connection.

## Mongoose compared with running lwIP and an HTTP library directly

The obvious alternative is to assemble the pieces yourself: lwIP for TCP/IP, a small HTTP parser, and mbedTLS for TLS. That route gives you maximum control and each component has its own release cadence and its own licence, which matters if your legal review treats GPLv2 as a blocker. The cost is integration work. You write the glue between lwIP's callbacks and your HTTP parser, you handle WebSocket framing yourself or add another library, and you repeat that work for each board. Mongoose's difference is that the HTTP, WebSocket, MQTT, SNTP and Modbus-TCP layers sit behind one event loop and one callback signature, so moving from an STM32 with the built-in stack to a Linux gateway is a build change rather than a rewrite. The README also notes the source is both ISO C and ISO C++ compliant, which matters for mixed firmware codebases. What you give up in exchange is the ability to swap out one layer; with Mongoose the layers move together.

## Licence, release cadence and the cost of staying current

The licence badge in the README reads GPLv2 or Commercial, and the repository's LICENSE file is reported by GitHub as NOASSERTION, so you should read the file itself rather than trust the badge. For a closed firmware product, GPLv2 is usually the deciding factor, and the README points to commercial support with proactive security updates and a CRA-compliance page. That is a business decision, not a technical one, and it is worth resolving before you write code against the API. On cadence, releases are frequent: 7.23 on 2026-08-12, 7.22 on 2026-06-24 and 7.21 on 2026-04-01. Frequent releases mean frequent upgrade decisions. Because Mongoose is vendored as two files rather than resolved by a package manager, an upgrade is a diff of mongoose.c and mongoose.h against your tree, and any local patch you made will conflict. The README does not document a rollback procedure, so plan on keeping the previous pair of files somewhere you can restore them from.

## Conclusion

Adopt cesanta/mongoose when you need an HTTP or MQTT endpoint on a microcontroller and want to avoid pulling in an RTOS plus a separate TCP/IP stack. Do not adopt it if your product cannot ship under GPLv2 and you are not prepared to buy the commercial licence, or if you need a full application framework rather than a network layer. Before committing, verify which built-in stack your chip is listed under in the README tables, confirm whether your TLS terminates in Mongoose or in mbedTLS or OpenSSL, and read the LICENSE file in the repository root, which the GitHub API reports as NOASSERTION rather than a clean GPLv2 identifier.

## FAQ

### How do I install cesanta/mongoose?

There is no installer. The README says to drop mongoose.c and mongoose.h into any C or C++ project, and the build comment in its first example is cc main.c mongoose.c.

### How do I use cesanta/mongoose to build an HTTP server?

Initialise a struct mg_mgr with mg_mgr_init, register a handler with mg_http_listen on a URL such as http://0.0.0.0:8000, then loop on mg_mgr_poll. Inside the handler, MG_EV_HTTP_MSG delivers a struct mg_http_message that you can pass to mg_http_serve_dir or answer with mg_http_reply.

### Does cesanta/mongoose work on Node.js, TypeScript, Next.js or Express?

No. Those questions refer to the npm package named mongoose, which is a MongoDB object modelling library for JavaScript. cesanta/mongoose is a C and C++ network library for embedded devices and desktop operating systems.

## Sources

- [cesanta/mongoose on GitHub](https://github.com/cesanta/mongoose)
- [Issues](https://github.com/cesanta/mongoose/issues)
- [Project website](https://mongoose.ws)
- [README](https://github.com/cesanta/mongoose/blob/master/README.md)
- [Releases](https://github.com/cesanta/mongoose/releases)

---

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