Cook the bacon: In a large pot or Dutch oven, cook the bacon over medium heat until crispy. Transfer to a plate lined with paper towels and reserve 2–3 tablespoons for topping. Carefully discard the excess bacon grease, leaving about 2-3 tablespoons in the pot.
Sauté the aromatics: Add the butter and diced onion to the pot. Cook for about 5 minutes, until the onion has softened. Add the garlic and cook for 30 seconds, until fragrant.
Make the roux: Sprinkle the flour over the onion and garlic. Stir well and cook for 1–2 minutes.
Add the liquids: Slowly whisk in the milk, followed by the chicken broth, until smooth and well combined.
Season: Stir in the chicken base or bouillon, black pepper, and cayenne pepper, if using. Season lightly with salt, keeping in mind that the bacon, broth, bouillon, and cheese will add saltiness.
Cook the potatoes: Add the potatoes and bring the soup to a boil. Reduce the heat to a simmer and cook for 15–20 minutes, or until the potatoes are fork-tender.
Mash: Use a potato masher to mash the potatoes to your desired consistency, leaving some chunks for texture.
Finish the soup: Reduce the heat to medium-low. Stir in the heavy cream, cheddar cheese, and most of the cooked bacon, reserving some for topping. Stir until the cheese is melted and the soup is creamy.
Serve: Ladle the soup into bowls and top with cheddar cheese, sour cream, reserved bacon, and green onions. Serve warm.