Suppose we have a Linux-based embedded device whose only job is to read data from sensors and publish that data to a cloud server over MQTT. Since the device is designed for this single, dedicated purpose, my understanding is that we don't need a full general-purpose Linux distribution.
In my opinion, the best approach is to treat it as a fully featured server, because it will be exposed to the internet (even if only "slightly").
(It is obviously possible to do it on a suitable microcontroller, say leveraging
mongoose, but I don't like the idea of trying to secure that against the security maelstorm that internet is.)
For a server (including NAS boxes), starting with minimal Debian install is a pretty good idea. Even if it is technically an "embedded device" or "appliance", I do believe these are best (security and reliability-wise) treated as plain Linux servers exposed to the internet. From minimal Debian, on top of the base system (filesystems, base util-linux stuff), you'll get a robust networking support (WiFi or Ethernet, DHCP client, DNS caching) et cetera. With WiFi or 4G/5G/LTE networking, you'll also want NetworkManager (and ModemManager, if you use an USB-connected 4G/5G/LTE module). You can use systemd for ease of maintenance: on a headless server with very few services and no interactive humans, it tends to do well. You will need TLS support for the data-gathering-and-publishing daemon; choose between
openssl,
gnutls,
mbedtls at least in Debian.
Since the data bandwidth is relatively low, you do not necessarily need to write the service in a compiled language like C, C++, Rust, etc., but can also use interpreted languages like Python.
I would focus here on
server-side development, making the service
robust, including testing it with severely limited run-time limits, executing your service daemon using
prlimit -m 200M yourdaemon... . Examine how it behaves when it starts running out of memory in particular, and how it handles loss of Internet access (including changes in local IP address then). And always test what happens when it is bombarded with fake connection attempts and such. You can simulate all that using local fixed IP addresses, and bringing up and taking down net interfaces using
ip (iproute2 package) or
ifconfig (net-tools package), and using netcat or socat (many alternatives) from a Bash one-liner loop to fake the connections.
One important point on Linux server side development, be it embedded or fully-featured: you
must read up on
POSIX.1, and its
system interfaces in particular, because that is what your C library (glibc, newlibc) your entire userspace will use already implements. The old K&R C, or the C11 or later as implemented by e.g. Microsoft, is a wooden mallet compared to POSIX C when it comes to networking and server-side software development. Even if your SBC/SoC has only one physical CPU core, using pthreads sensibly (with suitably small stacks in particular), you can get more out of that core; the per-pthread overhead in Linux (aside from default stack size, which tends to be huge, allocated for each thread) is very, very low.
Just as an example, you'll want to be able to use
getline(),
nftw(),
glob(),
fnmatch(),
regcomp()/regexec()/regfree(), POSIX
signals (and use
sigaction(), never
signal() or
sigvec()),
gettext() for internationalization/language versioning,
iconv_open()+
iconv() for character set conversions if necessary; and so on. Otherwise, you'll be wasting your time reinventing a poor wheel while a time-tested, reliable one is already included in the C library your userspace is based on. (Applies to both glibc and newlib, by the way.)