1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
|
# Motivation
The [RouterOS `container.npk` feature](https://help.mikrotik.com/docs/display/ROS/Container) is highly useful, but it is a custom development written in-house by MikroTik, not a copy of Docker Engine or any of the other server-grade container engines.(^Podman, LXC/LXD, etc.) Because of the stringent resource constraints on the bulk of MikroTik's devices, it is exceptionally small, thus unavoidably very thinly featured compared to its big-boy competition. If we can use installed size as a proxy for expected feature set size, we find:
The [RouterOS `container.npk` feature](https://help.mikrotik.com/docs/display/ROS/Container) is highly useful, but it is a custom development written in-house by MikroTik, not a copy of Docker Engine or any of the other server-grade container engines.(^Podman, LXC/LXD, etc.) Because of the stringent resource constraints on the bulk of MikroTik’s devices, it is exceptionally small, thus unavoidably very thinly featured compared to its big-boy competition. If we can use installed size as a proxy for expected feature set size, we find:
* **Docker Engine**: 422 MiB(^Version 27.1.1, according to `dnf remove docker-ce…` after installing these packages [per the instructions](https://docs.docker.com/engine/install/rhel/#install-docker-engine).)
* **`containerd`+`nerdctl`**: 174 MiB(^Version 2.0.0-rc1 of `nerdctl` plus the `containerd` from the Docker Engine CE install according to `sudo dnf remove containerd` and `du -sh nerdctl`.)
* **Podman**: 107 MiB(^Version 4.9.4 on EL9, according to `sudo dnf remove podman conmon crun`.)
* **systemd-nspawn**: 1.3 MiB(^This is the bare-bones OCI image runner built into systemd, with a feature set fairly close to that of `container.npk`. The size above is for version 252 of this program's parent `systemd-container` package as shipped on EL9.)
* **systemd-nspawn**: 1.3 MiB(^This is the bare-bones OCI image runner built into systemd, with a feature set fairly close to that of `container.npk`. The size above is for version 252 of this program’s parent `systemd-container` package as shipped on EL9.)
* **`container.npk`**: _0.0626 MiB_(^Version 7.15.2, according to `/system/package/print`.)
And this is fine! RouterOS serves a particular market, and its developers are working within those constraints. The intent here is to provide a mapping between what people expect of a fully-featured container engine and what you actually get in RouterOS. Where it makes sense, I try to provide workarounds for missing features and guidance to alternative methods where RouterOS's way merely *works* differently.
And this is fine! RouterOS serves a particular market, and its developers are working within those constraints. The intent here is to provide a mapping between what people expect of a fully-featured container engine and what you actually get in RouterOS. Where it makes sense, I try to provide workarounds for missing features and guidance to alternative methods where RouterOS’s way merely *works* differently.
# <a id="general"></a>General Observations
Allow me to begin with a distilled version of the details below, both to satisfy the **tl;dr** crowd and to set broad expectations for the rest of my readers.
The major things lacking in RouterOS's `container.npk` feature are:
The major things lacking in RouterOS’s `container.npk` feature are:
* a local image cache(^One knock-on effect of this not covered above is that removing and reinstalling a container requires RouterOS to re-download the image, even when done back-to-back, even if you never start the container between and thereby cause it to make changes to the expanded image's files.)
* a CoW/overlay file system(^This is not a verified fact, but an inference based on the observation that if RouterOS _did_ have this facility underlying its containers, I would expect to find equivalents to Docker's `commit` and `diff` commands. This pairs with the lack of an image cache: no CoW means no need for a baseline source to compute deltas against.)
* a local image cache(^One knock-on effect of this not covered above is that removing and reinstalling a container requires RouterOS to re-download the image, even when done back-to-back, even if you never start the container between and thereby cause it to make changes to the expanded image’s files.)
* a CoW/overlay file system(^This is not a verified fact, but an inference based on the observation that if RouterOS _did_ have this facility underlying its containers, I would expect to find equivalents to Docker’s `commit` and `diff` commands. This pairs with the lack of an image cache: no CoW means no need for a baseline source to compute deltas against.)
* image building
* orchestration
* JSON and REST APIs
* per-container limit controls:(^The only configurable resource limit is on maximum RAM usage, and it's global, not settable on a per-container basis.)
* per-container limit controls:(^The only configurable resource limit is on maximum RAM usage, and it’s global, not settable on a per-container basis.)
* FD count
* PID limit
* CPU usage
* storage IOPS
* `/dev/shm` size limit
* terminal/logging bps
* syscall restrictions
* [capability][caps] restrictions
* [rlimit]
* hardware pass-thru:
* USB device entries under `/dev` are on the wish list, but not currently available.(^Not unless RouterOS itself sees the USB device, as with storage media, which you can bind-mount into the container with "`/container/add mounts=…`".)
* USB device entries under `/dev` are on the wish list, but not currently available.(^Not unless RouterOS itself sees the USB device, as with storage media, which you can bind-mount into the container with “`/container/add mounts=…`”.)
* There is no GPU support, not even for bare-metal x86 installs.
[caps]: https://www.man7.org/linux/man-pages/man7/capabilities.7.html
[rlimit]: https://www.man7.org/linux/man-pages/man2/getrlimit.2.html
A good many of these limitations stem from those of RouterOS itself. For instance, while RouterOS proper is built atop Linux, and it provides a feature-rich CLI, it is nothing like a Linux command shell. This means equivalent commands to the likes of "`docker run --attach std…`" would not make a lot of sense on RouterOS, there being nothing like the termios/pty subsystem visible at the RouterOS CLI level.
A good many of these limitations stem from those of RouterOS itself. For instance, while RouterOS proper is built atop Linux, and it provides a feature-rich CLI, it is nothing like a Linux command shell. This means equivalent commands to the likes of “`docker run --attach std…`” would not make a lot of sense on RouterOS, there being nothing like the termios/pty subsystem visible at the RouterOS CLI level.
While I could also point out the lack of a background management daemon(^`containerd` in modern setups, `dockerd` in old ones) a good bit of Docker's competition also lacks this, on purpose, so I cannot ding RouterOS for this same lack.
While I could also point out the lack of a background management daemon(^`containerd` in modern setups, `dockerd` in old ones) a good bit of Docker’s competition also lacks this, on purpose, so I cannot ding RouterOS for this same lack.
With this grounding, let us dive into the details…
## <a id="create" name="load"></a>Container Creation
The single biggest area of difference between the likes of Docker and the RouterOS `container.npk` feature is how you create containers from OCI images.
RouterOS combines Docker's `create` and `load` commands as `/container/add`, the distinction expressed by whether you give it the `remote-image` or `file` option, respectively.
RouterOS combines Docker’s `create` and `load` commands as `/container/add`, the distinction expressed by whether you give it the `remote-image` or `file` option, respectively.
<a id="run"></a>There is no shorthand command like `docker run` for creating and starting a container in a single step. Moreover, the lack of Linux-like interactive terminal handling means a simple command like…
$ docker run --rm -it alpine:latest
…is most briefly expressed under RouterOS as…
> /container
> add remote-image=alpine:latest veth=veth1 entrypoint=sleep cmd=3600
… wait for it to download and extract …
… poll with "print" commands to get the container ID …
> start 0
… wait for it to launch …
> shell 0
sh-5.1# <do something inside the container>
sh-5.1# exit
> stop 0
> remove 0
The "sleep 3600" hack is necessary because RouterOS lacks any notion of interactive mode. In order to keep the container from starting, doing a whole lot of _nothing_, and then stopping(^…but not cleaning up after itself as with `--rm`, mind!) you have to give it some type of busy-work to keep it alive. This is a common one, but it only lasts an hour. More complicated methods are difficult to pull off due to the lack of host-side Bourne shell command parsing in RouterOS; the `cmd` option gets passed into the container as a single string, but you only have two to play with, `entrypoint` and `cmd`, so how do you say something like the following?
The “sleep 3600” hack is necessary because RouterOS lacks any notion of interactive mode. In order to keep the container from starting, doing a whole lot of _nothing_, and then stopping(^…but not cleaning up after itself as with `--rm`, mind!) you have to give it some type of busy-work to keep it alive. This is a common one, but it only lasts an hour. More complicated methods are difficult to pull off due to the lack of host-side Bourne shell command parsing in RouterOS; the `cmd` option gets passed into the container as a single string, but you only have two to play with, `entrypoint` and `cmd`, so how do you say something like the following?
docker run alpine:latest 'while true ; do sleep 5 ; done' &
The answer is that if you want a RouterOS container to do anything tricky like that, you need to write your own `ENTRYPOINT` script.
Now, given the size of the output from `docker create --help`, it should not be surprising that the bulk of that is either not available in RouterOS or exists in a very different form. Most of these limitations stem from the list of [generalities above](#general). For instance, the lack of any CPU usage limit features means there is no equivalent under `/container` for the several `docker create --cpu*` options. Rather than go into these options one by one, I'll cover the ones where the answers cannot be gleaned through a careful reading of the rest of this article:
Now, given the size of the output from `docker create --help`, it should not be surprising that the bulk of that is either not available in RouterOS or exists in a very different form. Most of these limitations stem from the list of [generalities above](#general). For instance, the lack of any CPU usage limit features means there is no equivalent under `/container` for the several `docker create --cpu*` options. Rather than go into these options one by one, I’ll cover the ones where the answers cannot be gleaned through a careful reading of the rest of this article:
* **`--env`**: The equivalent is this RouterOS command pair:
/container/envs/add name=NAME …
/container/add envlist=NAME …
This is in fact closer to the way the **`--env-file`** option works, except that under RouterOS, this particular "file" isn't stored under `/file`!
This is in fact closer to the way the **`--env-file`** option works, except that under RouterOS, this particular “file” isn’t stored under `/file`!
* **`--expose`/`--publish`**: <a id="publish"></a>The VETH you attach the container to makes every listening socket visible by default. It is left up to you to manually block off anything exposed against your wishes by use of `/ip/firewall/filter` commands.
* **`--health-cmd`**: Because health-checks are often implemented by periodic API calls to verify that the container continues to run properly, the logical equivalent under RouterOS is to [script] calls to [`/fetch`](https://help.mikrotik.com/docs/display/ROS/Fetch), which then issues `/container/{stop,start}` calls to remediate any problems it finds.
* **`--init`**: Although there is no direct equivalent to this in RouterOS, nothing stops you from doing it the old-school way, creating a container that calls "`ENTRYPOINT /sbin/init`" or similar, which then starts the subordinate services inside that container. It would be somewhat silly to use systemd for this in a container meant to run on RouterOS in particular; a more suitable alternative would be [Alpine's OpenRC](https://wiki.alpinelinux.org/wiki/OpenRC) init system, a popular option for managing in-container services.
* **`--init`**: Although there is no direct equivalent to this in RouterOS, nothing stops you from doing it the old-school way, creating a container that calls “`ENTRYPOINT /sbin/init`” or similar, which then starts the subordinate services inside that container. It would be somewhat silly to use systemd for this in a container meant to run on RouterOS in particular; a more suitable alternative would be [Alpine’s OpenRC](https://wiki.alpinelinux.org/wiki/OpenRC) init system, a popular option for managing in-container services.
* **`--label`**: The closest equivalent is RouterOS's `comment` facility, which you can apply to a running container with "`/container/set 0 comment=MYLABEL`".
* **`--label`**: The closest equivalent is RouterOS’s `comment` facility, which you can apply to a running container with “`/container/set 0 comment=MYLABEL`”.
* **`--mac-address`**: If RouterOS had this, I would expect it to be offered as "`/interface/veth/set mac-address=…`", but that does not currently exist. As it stands, a VETH interface's MAC address is random, same as the default behavior of Docker.
* **`--mac-address`**: If RouterOS had this, I would expect it to be offered as “`/interface/veth/set mac-address=…`”, but that does not currently exist. As it stands, a VETH interface’s MAC address is random, same as the default behavior of Docker.
* **`--network`**: This one is tricky. While there is certainly nothing like "`/container/add network=…`", it's fair to say the equivalent is, "RouterOS." You are, after all, running this container atop a highly featureful network operating system. Bare-bones the `container.npk` runtime may be, but any limitations you run into with the network it attaches to are more a reflection of your imagination and skill than to lack of command options under `/container`.
* **`--network`**: This one is tricky. While there is certainly nothing like “`/container/add network=…`”, it’s fair to say the equivalent is, “RouterOS.” You are, after all, running this container atop a highly featureful network operating system. Bare-bones the `container.npk` runtime may be, but any limitations you run into with the network it attaches to are more a reflection of your imagination and skill than to lack of command options under `/container`.
* **`--pid/userns/uts`**: The RouterOS container runner must use Linux namespaces under the hood, but it does not offer you control over which PID, file, network, etc. namespaces each container uses.
* **`--read-only`**: RouterOS offers precious little in terms of file system permission adjustment. As a rule, it is best to either shell into the container and adjust permissions there or rebuild the container with the permissions you want from go. Any expectations based on being able to adjust any of this between image download time and container creation time are likely to founder.
* **`--restart`**: <a id="restart"></a>The closest RouterOS gets to this is its `start-on-boot` setting, meaning you'd have to reboot the router to get the container to restart. If you want automatic restarts, you will have to [script] it.
* **`--restart`**: <a id="restart"></a>The closest RouterOS gets to this is its `start-on-boot` setting, meaning you’d have to reboot the router to get the container to restart. If you want automatic restarts, you will have to [script] it.
* **`--rm`**: No direct equivalent. There is a manual `/container/remove` command, but nothing like this option, which causes the container runtime to automatically remove the instantiated container after it exits. It's just as well since this option is most often used when running _ad hoc_ containers made from a previously downloaded image; RouterOS's lack of an image cache means you have to go out of your way to export a tarball of the image and upload it to the router, then use "`/container/add file=…`" if you want to avoid re-downloading the image from the repository on each relaunch.
* **`--rm`**: No direct equivalent. There is a manual `/container/remove` command, but nothing like this option, which causes the container runtime to automatically remove the instantiated container after it exits. It’s just as well since this option is most often used when running _ad hoc_ containers made from a previously downloaded image; RouterOS’s lack of an image cache means you have to go out of your way to export a tarball of the image and upload it to the router, then use “`/container/add file=…`” if you want to avoid re-downloading the image from the repository on each relaunch.
With all of that to ground us, the rest is far simpler to discuss…
[script]: https://help.mikrotik.com/docs/display/ROS/Scripting
# <a id="tlc"></a>Remaining Top-Level Commands
For lack of any better organization principle, I've chosen to cover these commands in alphabetical order. I skip over short aliases like `docker rmi` for `docker image rm` in order to cover things only once, and I don't repeat any of the `create`/`load`/`run` discussion [above](#create). Because Podman cloned the Docker CLI, this matches fairly well with it, except that I do not currently go into any of its pure extensions, such as its eponymous `pod` command.
For lack of any better organization principle, I’ve chosen to cover these commands in alphabetical order. I skip over short aliases like `docker rmi` for `docker image rm` in order to cover things only once, and I don’t repeat any of the `create`/`load`/`run` discussion [above](#create). Because Podman cloned the Docker CLI, this matches fairly well with it, except that I do not currently go into any of its pure extensions, such as its eponymous `pod` command.
## <a id="attach"></a>`attach`
There is no interactive terminal (stdin/stdout/stderr) in RouterOS to speak of, and you normally run these boxes headless, connecting to their virtual terminal via WinBox or SSH only long enough to reconfigure something before logging back out. The `container.npk` feature is designed to run its subordinate processes purely in the background, with logging suppressed by default. If you say `/container/set logging=yes`, the standard output streams go to the configured logging destination, but there is no way to interactively type commands at the container short of `/container/shell`, which carries the requirement that a `/bin/sh` program exist inside the container.(^You can't count on that in every container. Indeed, all of [my public containers](https://hub.docker.com/repositories/tangentsoft) elide the shell to reduce the container's attack surface.) Even then, you're typing commands at the shell, not at the container's `ENTRYPOINT` process.
There is no interactive terminal (stdin/stdout/stderr) in RouterOS to speak of, and you normally run these boxes headless, connecting to their virtual terminal via WinBox or SSH only long enough to reconfigure something before logging back out. The `container.npk` feature is designed to run its subordinate processes purely in the background, with logging suppressed by default. If you say `/container/set logging=yes`, the standard output streams go to the configured logging destination, but there is no way to interactively type commands at the container short of `/container/shell`, which carries the requirement that a `/bin/sh` program exist inside the container.(^You can’t count on that in every container. Indeed, all of [my public containers](https://hub.docker.com/repositories/tangentsoft) elide the shell to reduce the container’s attack surface.) Even then, you’re typing commands at the shell, not at the container’s `ENTRYPOINT` process.
All of this explains why [RouterOS lacks a direct equivalent `docker run`](#run), particularly the common `-it` option pair. The closest `container.npk` comes is its [`shell`](#shell) command implementation.
All of this explains why RouterOS [lacks](#run) a direct equivalent of “`docker run`”, particularly the common `-it` option pair. The closest `container.npk` comes is its [`shell`](#shell) command implementation.
## <a id="build"></a>`build`/`buildx`
RouterOS provides a bare-bones container runtime only, not any of the image building toolchain. It is closer in nature to the `runc` command underlying `containerd` than to Docker Engine proper. An even closer match is the lightweight `crun` command at the heart of Podman, and even more so the elementary runner that ships with systemd, variously called either [`systemd-nspawn`][sdnsp] or [`systemd-container`][sdcnt], depending on the tastes of whoever is packaging it.
[sdcnt]: https://packages.fedoraproject.org/pkgs/systemd/systemd-container/
|
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
|
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
|
-
+
-
+
-
+
-
+
|
## <a id="import"></a>`import`
This is `/container/add file=oci-image.tar` in RouterOS.
## <a id="info"></a>`info`
With the understanding that RouterOS has far fewer configurables than a big-boy container engine, the closest command to this in RouterOS is `/container/config/print`. The output is in typical RouterOS "print" format, not JSON.
With the understanding that RouterOS has far fewer configurables than a big-boy container engine, the closest command to this in RouterOS is `/container/config/print`. The output is in typical RouterOS “print” format, not JSON.
## <a id="inspect"></a>`inspect`
The closest approximation to this in RouterOS is
/container/print detail where …
You get only a few lines of information back from this, mainly what you gave it to create the container from the image. You will not get the pages of JSON data the Docker CLI gives.
A related limitation is that the configurable items are often global in RouterOS, set for all containers running on the box, not available to be set on a per-container basis. A good example of this is the memory limit, set via `/container/config/set ram-high=…`.
## <a id="kill" name="stop"></a>`kill`/`stop`
RouterOS doesn't make a distinction between "kill" and "stop". The `/container/stop` command behaves more like `docker kill` or `docker stop -t0` in that it doesn't try to bring the container down gracefully before giving up and killing it.
RouterOS doesn’t make a distinction between “kill” and “stop”. The `/container/stop` command behaves more like `docker kill` or `docker stop -t0` in that it doesn’t try to bring the container down gracefully before giving up and killing it.
## <a id="login"></a>`login`/`logout`
RouterOS only allows you to configure a single image registry, including the login parameters:
/container/config/set registry-url=… username=… password=…
The only way to "log out" is to overwrite the username and password via:
The only way to “log out” is to overwrite the username and password via:
/container/config/set username="" password=""
## <a id="logs"></a>`logs`
By default, RouterOS drops all logging output from a container. To see it, you must enable it on a per-container basis with the `/container/add logging=yes` option, then tell RouterOS where to send those logs via a `/system/logging add topics=container …` command.
Each message is handled in real time, not buffered as with Docker or Podman. Furthermore, RouterOS mixes logs from all sources for a given "topic" set, which in this context means that if you have multiple running containers on the device, their logs all go to the same place. Thus, if you were expecting to be able to set up memory logging for a container, log out of the router, then sometime later come back in and get a dump of everything that one particular container has logged since the last time you asked — as you can with the big-boy container engines — then you will be disappointed.
Each message is handled in real time, not buffered as with Docker or Podman. Furthermore, RouterOS mixes logs from all sources for a given “topic” set, which in this context means that if you have multiple running containers on the device, their logs all go to the same place. Thus, if you were expecting to be able to set up memory logging for a container, log out of the router, then sometime later come back in and get a dump of everything that one particular container has logged since the last time you asked — as you can with the big-boy container engines — then you will be disappointed.
## <a id="pause"></a>`pause`/`unpause`
No such feature in RouterOS; a container is running or not.
If the container has a shell, you could try a command sequence like this to get the same effect:
|
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
|
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
|
-
+
-
+
-
+
-
+
-
+
|
While it _can_ pull from an OCI image repo, it does so as part of `/container/add`, which is closer to a `docker create` command than to `docker pull`.
There is no equivalent at all to `docker push`.
## <a id="rename"></a>`rename`
RouterOS doesn't let you set the name on creation, much less rename it later. The closest you can come to this is to add a custom `comment`, which you can both set at "`add`" time and after creation.
RouterOS doesn’t let you set the name on creation, much less rename it later. The closest you can come to this is to add a custom `comment`, which you can both set at “`add`” time and after creation.
## <a id="restart"></a>`restart`
See [`--restart`](#restart) above.
## <a id="rm"></a>`rm`
RouterOS spells this `/container/remove`, but do be aware, there is no equivalent for `docker rm -f` to force the removal of a running container. RouterOS makes you stop it first.
## <a id="search"></a>`search`
There is no equivalent to this in RouterOS. You will need to connect to your image registry of choice and use its search engine.
## <a id="secret"></a>`secret`
This typically shows up as part of Docker Swarm, Kubernetes, or Podman pods, none of which exists under RouterOS, which is why it shouldn't surprise you that RouterOS has no secret-sharing facility. The standard fallback for this is passed-in environment variables or bind-mounted volumes.
This typically shows up as part of Docker Swarm, Kubernetes, or Podman pods, none of which exists under RouterOS, which is why it shouldn’t surprise you that RouterOS has no secret-sharing facility. The standard fallback for this is passed-in environment variables or bind-mounted volumes.
## <a id="start"></a>`start`
RouterOS has `/container/start`, with limitations you can reasonably infer from the rest of this article.
## <a id="swarm"></a>`swarm`
Extending from the lack of single-box container orchestration features, RouterOS also completely lacks _cluster_ orchestration. It doesn't even have a lightweight one like [Docker Swarm](https://docs.docker.com/engine/swarm/) or [k3s](https://k3s.io), and it certainly doesn't support the behemoth that is Kubernetes.
Extending from the lack of single-box container orchestration features, RouterOS also completely lacks _cluster_ orchestration. It doesn’t even have a lightweight one like [Docker Swarm](https://docs.docker.com/engine/swarm/) or [k3s](https://k3s.io), and it certainly doesn’t support the behemoth that is Kubernetes.
## <a id="tag"></a>`tag`
RouterOS does nothing more with tags than to select which image to download from a registry. Without a local image cache, you cannot re-tag an image.
## <a id="update"></a>`update`
No equivalent short of this:
/container/stop 0
…wait for it to stop…
/container/remove 0
/container/add …
The last step is the tricky one since `/container/print` shows most but not all of the options you gave to create it. If you didn't write down how you did that, you're going to have to work that out to complete the command sequence.
The last step is the tricky one since `/container/print` shows most but not all of the options you gave to create it. If you didn’t write down how you did that, you’re going to have to work that out to complete the command sequence.
## <a id="version"></a>`version`
While RouterOS's `container.npk` technically does have an independent version number of its own, it is meant to always match that of the `routeros.npk` package you have installed. RouterOS automatically upgrades both in lock-step, making this the closest equivalent command:
While RouterOS’s `container.npk` technically does have an independent version number of its own, it is meant to always match that of the `routeros.npk` package you have installed. RouterOS automatically upgrades both in lock-step, making this the closest equivalent command:
/system/package/print
## <a id="wait"></a>`wait`
The closest equivalent to this would be to call `/container/stop` in a RouterOS script and then poll on `/container/print where …` until it stopped.
|