Replicating the entire iocage as a single task is doable, and all from the gui.
It's just utterly and completely opaque, non obvious, and not facilitated. It's like the idea never ocurred that anyone would ever want to do it, which boggles my mind...
Anyway... the secret was to create the destination top level dataset, empty and unmounted, then create a recursive snapshot of the source manually, but clear out the snapshot name and select the "auto-..." naming scheme, then create the scheduled snapshot task, then the replication task. Then you can use the "run now" button on the replication task.
One source of confusion is when you use the gui in basic mode to create the replication job, it will do that initial snapshot one time automatically behind the scene without saying much about it, but only that first time. It doesn't do it in response to the "run now" button. So if you edit the job to use any other source, or use advanced mode instead of basic when creating a new job, it doesn't run the initial snapshot. And the scheduled snapshot page doesn't have a "run now" button, it just says "pending" and it'll run whenever the next schedule comes around and that's it.
So to make "run now" work, you have to create the snapshot that the replication wants to read.
And the next non-obvious part is that the snapshot NAMES need to match what the replication job is looking for. So when you use the snapshot page to manually make a snapshot, by default it will use a name "manual-...", and the replication job will ignore that and say "no snapshots available...". So what you do is blank out that name field and leave it empty, then on the field below that you select the "auto-..." naming scheme from a pulldown list (that only has that one item in it). That will create a manual snapshot on the spot, but with names that are the same as the sheduled snapshots.
Once those exist, and the destination dataset exists, empty, not mounted, then the "run now" button on the replication task works.
If the destination dataset already exists with stuff in it from prior messing around, you have to clear it all out and finally get it unmounted first.
It may take several manual steps because there may be datasets within datasets and half of them may be "busy" just from being mounted or not empty. By "destination dataset" I do not mean "tank" or your equivalent (Mine happens to be named "v1" where most examples seem to have "tank"), I mean some dataset within that like tank/iocage or tank/iocage/jails. Start at Storage -> Pool -> tank -> <destination> and expand all datasets within there and try to delete them. If any can't be deleted, use a root sheel to unmount. "umount /mnt/tank/iocage/jails/junk", then try to delete from the gui again. Repeat until you have tank/iocage and nothing in it. Use the shell to unmount the top-level empty destination "umount /mnt/tank/iocage"
Or if the destination pool is already empty and a destination dataset doesn't exist yet, just create it, and it will be empty and unmounted.
I will show the explicit example from my own case just to show an explicit example, but yours will be different.
Where most examples have "tank" my main (big, slow) pool happens to be named "v1". This is where I want my destination to go.
My source is my "iocage" dataset from a pool named "ssd"
I have a pair of ssd's set up as an unsafe-but-fast raid0 pool named "ssd" for jails.
Since this pool is unsafe, I want to back up my entire iocage from ssd/iocage to v1/iocage
So remember, in the following:
ssd = source pool
ssd/iocage = source dataset
v1 = destination pool
v1/iocage = destination dataset
Assuming the simpler case where you just create a new destination dataset on the destination pool and don't have to worry about umounting or deleting stuff...
1 - Storage -> Pools -> v1 -> v1 -> (3-dots) -> Add Dataset -> "iocage"
2 - Storage -> Snapshots -> Add
Dataset: ssd/iocage
Name: blank this out, erase the pre-loaded "manual-2022-..."
Naming Schema: auto-%Y-%m-%d_%H-%M
[x] Recursive
When you hit Submit on this, the snapshots will be created instantly, so they will already be available for the next steps to use.
The next is to make a periodic snapshot task exactly the same as the manual one above
3 - Tasks -> Periodic Snapshot Tasks -> Add
Dataset: ssd/iocage
[x] Recursive
Set whatever schedule and retention policy you want, just leave the naming schema alone so the default "auto-..." matches the other steps.
4 - Tasks -> Replication Tasks -> Add -> Advanced
Name: "ssd/iocage -> v1/iocage" (free form, can be anything)
Transport: LOCAL
Source: ssd/iocage
Destination: v1/iocage
[x] Run Automatically
Save
Now the "run now" button should work.
In my case I also selected the "almost full filesystem" and the "synchronize destination snapshots" options but be careful you only do that if you are sure the source and destination are correct because it will DELETE DATA from the destination. Don't cry to me if your tank goes poof.
Finally, the "run now" button only ever sends the source snapshots that already exist, it doesn't update the source snapshots. When you re-run "run now" no data changes on the destination unless the snapshots have been re-run. The scheduled replication updates the destination data only because the sheduled snapshot tasks has updated the source snapshots. If you want to do "update my backup now" you'll have to do a manual snapshot per step 2 above, and then "run now" on the replication.