There are a few alternatives:
A low-cost (but slow) method is two hook up one MCU input to a GPIO-output of a single-board-computer (SBC) that runs Linux and synchronizes its clock via NTP. On your SBC you run a small program that toggles the GPIO-output, waits 10**6
seconds (~ 11.574 days) and toggles the output again. You MCU reads on each toggle the epoch (in seconds) of the RTC and computes the duration (d) between start and stop.
The correction offset (off, unit: ppm) is then:
off = (d / 10**6 - 1) * 10**6
Costs: the cost of an SBC such as a raspberry PI.
A faster but also cheap alternative is to hook up a DCF77 receiver (or similar short-wave time-synchronisation service receiver, depending on you region) and use that to count the ticks of your MCU's RTC clockout pin (a.k.a. SQW pin, RTCOUT) in a time period of several minutes. The faster you can configure the clockout frequency, the shorter the time span you need to observe (for getting an accurate calibration offset).
For example with a clockout of 2**15 Hz a measurement period of 20 to 50 minutes should be sufficient (to average out errors in the time signal, interrupt jitter etc.).
You can then compute the calibration offset (off, unit ppm) via:
off = (measured / expected - 1) * 10**6
where
measured = counted ticks on the clockout pin
expected = #DCF77_minutes * 60 * freq
freq = e.g. 2**15 Hz
Costs: DCF77 receiver (or similar) can be obtained for less than 10 € (as of 2022), you might even scavenge a DCF77 module off an old alarm clock.
NB: If you work with a clockout of 2**15 Hz, your MCU need to support 32 bit wide timer registers - or the chaining of two 16 timer registers (e.g. where another timer counts the overflows of the first one and thus contains the most significant half of the timer).