Prep oven + dish. Preheat oven to 350°F and position rack in the lower third. Butter a 9x13-inch baking dish.
Cook aromatics. Melt butter in a large saucepan over medium heat. Add onion and cook until soft and lightly golden, about 5–7 minutes. Stir in garlic and cook for 30 seconds.
Make roux. Sprinkle flour over the onion mixture and whisk constantly for 1 minute to cook off the raw flour taste.
Build sauce. Slowly whisk in milk and heavy cream until smooth. Add salt, pepper, and thyme (if using). Cook, whisking often, until the sauce thickens slightly and coats the back of a spoon. Remove from heat.
Layer casserole. Arrange half the sliced potatoes in the baking dish. Top with half the ham, then pour over half the sauce. Repeat with remaining potatoes, ham, and sauce.
Bake covered. Cover loosely with foil and bake for 75–90 minutes, or until potatoes are fork-tender.
Finish uncovered. Remove foil, sprinkle cheese evenly over the top (if using), and bake uncovered for 15–20 minutes until bubbly and lightly golden.
Rest + serve. Let stand 10–15 minutes before serving so the sauce thickens and sets.