Optimizing Print Quality on a Prusa MK3S

With our small farm of 3D printers used to make the Grain Belt Models line of products and other accessories for various products, a way to get consistency not only from print to print was needed, but also from printer to printer.  After a couple years of fighting with inconsistency, a process was developed that is outlined below.  I can’t take credit for most of this – many of the ideas and steps are borrowed from others and/or adapted and improved.  Credit goes to them.  This is just a compilation of what works for us and some lessons learned.

We have standardized on the Prusa i3 MK3S line of 3D printers for our production printing.  These are good, solid printers with great support.  However, the print quality and, more importantly, consistency has more to do with the setup and calibration than the printer itself.  Obviously, you can’t have consistency if the printer is junk, but a great printer still needs some care and feeding to keep it operating at the top of its game.  The process we use can be broken down into three major steps:

  1. Flatten the bed
  2. Calibrate the first layer Z-height
  3. Wash, rinse, and repeat

Step 1 – Flatten The Bed

Although the Prusa i3 MK3S has a built-in mesh bed leveling algorithm, it’s not perfect.  The mesh bed leveling allows the printer to follow the topography of the bed and put down a consistent line of plastic.  This makes sure the first (and subsequent) layers have consistent layer height – needed for proper adhesion to the bed and to eliminate excess horizontal squish (elephant foot) of the filament.  However,  your finished item will still end up with the variation of the out-of-level bed in its surface.  If the bed varies 2mm over the footprint of the object, the object will be out-of-flat by that same amount.

Furthermore, the mesh bed leveling cannot remove all the error.  It works by measuring the bed height at a series of points across the bed.  For everywhere in between, and interpolation equation is used.  This is never perfect.  Although vastly simplified, think of it this way: if the mesh bed leveling can remove 90% of the error, and if the bed physically has 2mm of variation, you’ll end up with 0.2mm of residual (corrected) flatness after leveling.  However, if the bed starts with only 0.2mm of variation, you’ll end up with 0.02mm of residual (corrected) flatness.  What does this mean?  It’s far better to have a physically flat bed than to rely on the mesh bed leveling algorithm.

So, how do we get a physically flat bed?

Fortunately, this problem has already been solved thanks to user dumsumguy on Reddit.  Welcome to the Nylock Mod.  The basic gist is to put nylock nuts on the screws that attach the bed to the bed carriage.  The nylock nuts go on the bottom side of the bed and hold the screws to the bed – firmly so they aren’t loose, but can still turn relatively freely.  The middle screw uses the stock spacer and is tightened down normally, but the perimeter screws can now be turned into the holes in the carriage to move the bed up and down with the goal of achieving a state of perfect physical flatness.

The steps for performing the mod can be found at the Reddit link above and are reprinted below with some updates and commentary:

  1. Power off printer.

  2. Remove the print bed.  Be very careful not to pull on the wiring attached to it.

  3. Replace the 8 spacers around the outside of the bed with M3 nylock nuts.  Tighten them to the point where the screw won’t turn, then back off slightly so you can turn the screw but with some resistance.  You want the screw held firmly to the bed (no slop) but still need the ability to turn the screw.

  4. Place a toothpick in the center screw hole of the bed carriage, then put the spacer around it.

  5. Put the print bed back into place with the toothpick going through the middle hole and the outer screws aligned with their respective holes.  Remove the toothpick (without dislodging the spacer!) and put the center screw in place.  Turn just enough to engage the threads, but no more!  You don’t want to bend the bed too much in the middle – just enough to hold it.

  6. Go around the outside of the bed in a circle tightening the screws back into place, ONE TURN EACH!!!!  This ensures you get it attached without unnecessary stress on the bed.

  7. Repeat step 6 until the print bed is getting very close to the spacer that you left on the center hole.

  8. Use needle nose pliers to test fit one of the unused spacers at each perimeter screw.  Tighten each screw until you can’t pull the spacer out and then loosen the screw slightly so the spacer can be removed.

  9. Screw in the center screw until tight.

Now that the bed is reattached, with the ability to adjust its flatness, it is now time to make it truly flat.  The steps in the Reddit link above explain how to do this using a terminal and the raw output from the printer.  However, we have been using OctoPrint (on a Raspberry Pi running OctoPi) and there is a handy plugin for it that makes leveling the bed much easier: the Prusa Leveling Guide.  With this plugin, it heats the bed, runs the printer’s mesh leveling algorithm, extracts the raw data from it, and tells you how much to turn each of the perimeter screws to get a perfectly level bed.  Very easy!

To use the Prusa Leveling Guide, first make sure the steel sheet you’ll be using is on the bed, you’ve done an initial first-layer calibration (we’ll fine tune that later), and the appropriate steel sheet is selected on the printer.  Next, select a temperature profile.  We use PLA as a standard since most of the stuff we care about is printed using PLA.  But, if you’re printing something else with a different bed temperature, it’s best to do the leveling at that temperature.  Start the Prusa Leveling Guide and let the bed reach temperature.  Once at temperature, the plugin will automatically run the mesh leveling.  Once complete, the eight outer boxes will display how much, and which direction, to turn each of those screws.  You can choose whether to get the turn information in degrees, decimal turns, or fractional turns (I prefer degrees).


Above is the Prusa Leveling Guide output for a somewhat out-of-level bed.  In this case, for example, you would want to turn the front right screw 157 degrees counter-clockwise.  Similarly, a few of the other screws could also use adjustment.  At this stage, I usually ignore any screws reporting values less than about 30 degrees.  Get the big adjustments done first – you can fine tune on subsequent runs.  To adjust the screws, remove the steel sheet, turn the screws as indicated, and replace the steel sheet.  Click continue in the Prusa Leveling Guide and repeat the process until the bed is level.  I typically aim for about 0.1mm total bed variance.  Any less than that and you’ll end up chasing it – the noise in the measurements and the run-to-run variation make trying to get much lower than 0.1mm pointless.  Below is the output from a nicely leveled bed:


Step 2 – Calibrate the First Layer Z-height

Now that we have a perfectly (well, almost perfectly) level bed, it’s time to fine tune the first layer’s Z-height.  While you should always run the printer’s first layer calibration whenever you change the nozzle, adjust the PINDA probe, or change steel sheets, that calibration is very qualitative.  It gets you in the ballpark, but is far from adequate for production-quality consistency.  Therefore a better process is needed.

An ideal first layer height is one that provides enough “squish” of the filament onto the printing bed to get sufficient adhesion, but not too much squish that the filament creates blobs on either side of the nozzle.  With the optimum first layer height, you will get a nice, smooth oval cross-section extrusion.


For the Prusa i3 MK3S, the default first layer height is 0.2mm.  Although the printer tries to position the nozzle 0.2mm above the bed using the PINDA probe, that measurement is nowhere near accurate enough to do so blindly.  Therefore, you have to calibrate that height.  When the nozzle is more than 0.2mm from the bed, the thickness of the first layer will be more than 0.2mm.  When the nozzle is lower than 0.2mm from the bed, the first layer thickness will also be more than 0.2mm due to the “blobs” created on each side of the nozzle.  Only when the distance is perfectly 0.2mm will the first layer thickness actually be 0.2mm.  Well, almost…

The thickness of the first layer is also dependent on the amount of plastic extruded.  While the printer defaults and PrusaSlicer make reasonable assumptions to achieve this goal, it will never be perfect.  Extrusion speed, actual filament diameter, and other factors can contribute to extruding more (or less) filament than expected.  While you can calibrate all of these variables, too, we’ve found that not to be necessary (at least to date) to get a consistent, production-quality, print.  However, that does mean the optimal first layer height won’t be exactly 0.2mm.  But, that’s not a problem.  Since the optimal height occurs when the resulting thickness is at a minimum (remember above?), all we have to do is look for the Z-height that minimizes the resulting first layer thickness.

Several people have developed various calibration squares, and other objects to print, in an effort to accomplish this goal.  However, I never found any of them to have the needed consistency.  The primary problem was that each square (at each different Z-height) was a separate print.  Variations in bed/nozzle/room temperature between each print run, combined with the temperature coefficient of the PINDA probe and its non-perfect temperature compensation, made it hard to distinguish the characteristic minimum thickness.  The results were numerically “noisy”.  That led to the creation of our calibration strips (sliced for PLA and PETG):

cal-strips v1.gcode

cal-strips v1(petg).gcode

This prints 11 strips, with Z height ranging from -0.05mm to +0.05mm around the current Live Adjust Z setting of the printer (Settings -> Live adjust Z).  The first strip printed is -0.05mm from the current Live Adjust Z and each successive strip is 0.01mm higher.  This way you get a range of Z heights, all printed with the same initial PINDA height measurement(s), resulting in a much more consistent print that can now be quantitatively analyzed.

Once the print is done, mark the center strip with the current Live Adjust Z setting (so you don’t forget) and remove the strips from the bed.  In the photo below, all the strips have been marked to illustrate the range of Z heights, with the center strip value (-0.410mm) being the current Live Adjust Z setting on the printer.


Next, measure the thickness of each strip.  While it shouldn’t matter (you did level the bed, right?), try to measure in the same location on each strip.  We use a Mitutoyo 293-340-30 Digital Micrometer, but any good quality micrometer capable of 0.001mm (or better) resolution and repeatability should suffice.


Finally, plot each measurement against the Z height of the strip and look for the minimum.  In this case, the optimal value is at a Z-height of -0.380mm:


Note: The numbers in the “Thickness” column above are in microns.  That’s used as a shorthand from the micrometer to minimize typing…

If you don’t see a minimum, then you’re likely not in range of the optimum Z height.  Change the Live Adjust Z setting and re-run the test.  However, if you’re lucky, you will see a clear minimum in the plot.  That’s your optimum Live Adjust Z setting.  Change the printer to match that value, and you’re ready to go!

Step 3 – Wash, Rinse, and Repeat

Following the steps above should get you well on the way to better prints.  Once calibrated, the printer should require little attention.  However, there are a few things to keep in mind going forward:

  1. It’s always good practice to check the printer periodically.  Run the Prusa Leveling Guide to make sure the bed is still flat and print the Z-height calibration strips to make sure the printer is still centered.  Things do shift over time with variations in ambient temperature, moving the printer, or just minor shifts in the PINDA probe position.  Weekly is ideal, but even monthly is a good target.
  2. If you change steel sheets, you will need to repeat the Z-height calibration process for that sheet.  Even the opposite side of a sheet should have its own Z-height settings.  Fortunately, the latest Prusa firmware has provisions for storing Z-height values for multiple sheets, making it easy to switch between them.
  3. Clean, clean, clean.  Keep the steel sheet clean.  This cannot be stressed enough.  Isopropyl alcohol works well for general cleaning.  Wipe down the sheet before every print using a clean paper towel.  We use Bounty as they seem to be free of any “extras” that can be dissolved by the alcohol and deposited on the bed.  Don’t use the first few sheets, or the last few sheets, though, as the glue used to hold the roll together can be dissolved and contaminate the sheet.  Shop towels were also found to contain something in them that the alcohol dissolves – avoid them.
  4. If the print quality starts to suffer, first check the bed level and Z-height.  Often times poor print quality can be traced to some shift over time in one (or both) of these.  However, if that doesn’t cure the problem, then try a deeper cleaning on the sheet.  This should only be used as a last resort.  First, wash the sheet with dish soap and water.  This removes oils, salt, and sugar compounds that do eventually build up on the sheet but may not be removed by the isopropyl alcohol.  These contaminants affect how well the plastic sticks to the sheet.  After drying, give the sheet a quick wipe with acetone.  Use gloves, though, as skin oils can be dissolved by the acetone and deposited on the sheet, defeating the purpose.  Finally, wipe it down with isopropyl alcohol as you would before any print.  Try to handle the sheet only by the edges after cleaning.


The gcode above for the calibration strips is sliced for PLA and PETG.  If you’d like to use it with a different filament, you can use the STL file below and reslice it in PrusaSlicer:

cal-strips v1.stl

However, once the new gcode is generated, you will need to modify the Z-height for each strip.  While this can be done manually, this Python script does it automatically.


  1. Thanks SO much for publishing! I’m on a PC, and running Python with any success is a chore. Any chance you could slice this GCode out PETG. I have a TON to print in PETG, and your strips are fantastic. If you feel like getting crazy, a TPU version would be cool too! (both are defaults in PrusaSlicer)

    Thank you!!!!

  2. If you pull the above .stl file into PrusaSlicer, you can just change what type of filament you are using, reslice it, and save to a separate .stl.

  3. cal-strips gives me a great 1st layer for PLA. It’s not obvious to me where to manually change the Gcode to produce a PETG version. For those of us non-programmers could you publish the PETG cal-strips Gcode? It would be sincerely appreciated.

Leave a Reply

Your email address will not be published. Required fields are marked *