In a large pot, melt the butter over medium heat. Add the onions, celery, and garlic. Cook until the vegetables are softened, about 5-7 minutes.
Sprinkle the flour over the vegetables and stir to combine. Cook for an additional 1-2 minutes to remove the raw flour taste.
Gradually add the broth, stirring constantly to prevent lumps from forming.
Add the diced potatoes to the pot. Bring the mixture to a boil, then reduce heat and simmer for about 15-20 minutes, or until the potatoes are tender when pierced with a fork.
Using a potato masher or immersion blender, mash or blend some of the potatoes in the pot. This will help thicken the soup and create a creamy texture. If using an immersion blender, be careful not to over-blend – you want to leave some potato chunks for texture.
Stir in the milk and continue to simmer for an additional 5 minutes.
Season with salt and pepper to taste.
Serve hot, garnished with your choice of toppings.