Match ends, Birmingham City 0, Millwall 1.
Millwall secured their third straight away win as Fred Onyedinma's late strike was enough to beat Birmingham.
Blues goalkeeper David Stockdale made a fine save to deny Onyedinma, but he finished at the second attempt.
The Lions went closest in the first half when Marc Roberts headed George Saville's cross-shot off the line.
City created plenty of openings, but failed to get a shot on target until stoppage time and have now failed to score in 16 of their 32 league matches.
Both sides came into the game in good form, with Blues winning four of their previous seven league matches and Millwall unbeaten in five, and their confidence showed with chances created at both ends.
Birmingham had the better of an entertaining first half, with Craig Gardner's effort deflected past the post by Saville, and Jeremie Boga's free-kick narrowly off-target.
But Millwall, who had failed to win away from home in their first 13 attempts this season, survived the pressure and their threat grew after the break, with Stockdale forced to save from Jake Cooper following Onyedinma's opener.
The hosts had almost two-thirds of possession and 15 efforts on goal, but the Championship's lowest scorers (22) landed their only shot on target in the third minute of stoppage time when Jordan Archer blocked Harlee Dean's low effort.
Millwall, who secured their first league double over Birmingham in 19 attempts, stay 14th, while Birmingham move up a place to 19th courtesy of Bolton's 2-0 defeat at QPR.
Birmingham manager Steve Cotterill:
"We didn't play with enough zip or enthusiasm and this is what disappointed me. We didn't play badly, but we didn't perform anywhere near the level we have previously achieved. We looked flat and the ground was quiet with no atmosphere.
"We need to go on another good run, but it has never been plain sailing. Unfortunately it is going to be topsy-turvy for the rest of the season.
"It's not nice to accept that, but we've been down at the bottom since day one. It is a difficult job but one I've got to do."
Millwall boss Neil Harris:
"He (Onyedinma) is up and down with his performances because he is young. But when he comes off the bench with 20 or 25 minutes to go that is the kind of impact I demand as a manager.
"I was a little critical of our first-half performance. The second half was much better with a really good goal and we could have had two of three more.
"It's three wins in a row on the road, which isn't bad for a team which couldn't win away from home at the start of the season."
- 21LoweSubstituted forJotaat 45'minutes
- 6KieftenbeldBooked at 56mins
- 19MaghomaSubstituted forJutkiewiczat 81'minutes
- 20BogaSubstituted forAdamsat 74'minutes
- 18GallagherBooked at 26mins
- 4HutchinsonBooked at 90mins
- 14J Wallace
- 44MarshallSubstituted forOnyedinmaat 66'minutes
- 9GregorySubstituted forShackellat 90+3'minutes
- 20MorisonSubstituted forCahillat 85'minutes
- Darren England
- Shots on Target
Second Half ends, Birmingham City 0, Millwall 1.
Attempt saved. Fred Onyedinma (Millwall) right footed shot from a difficult angle on the left is saved in the centre of the goal. Assisted by Shaun Williams.
Maxime Colin (Birmingham City) wins a free kick in the defensive half.
Foul by Jed Wallace (Millwall).
Attempt blocked. Harlee Dean (Birmingham City) right footed shot from outside the box is blocked.
Attempt saved. Craig Gardner (Birmingham City) right footed shot from outside the box is saved in the bottom left corner.
Substitution, Millwall. Jason Shackell replaces Lee Gregory.
Shaun Hutchinson (Millwall) is shown the yellow card for a bad foul.
Sam Gallagher (Birmingham City) wins a free kick in the attacking half.
Foul by Shaun Hutchinson (Millwall).
Attempt missed. Jota (Birmingham City) left footed shot from the centre of the box is high and wide to the left following a set piece situation.
Harlee Dean (Birmingham City) wins a free kick in the attacking half.
Foul by George Saville (Millwall).
Carl Jenkinson (Birmingham City) wins a free kick in the defensive half.
Foul by Tim Cahill (Millwall).
Attempt missed. Shaun Hutchinson (Millwall) header from the centre of the box misses to the right. Assisted by Tim Cahill with a headed pass following a corner.
Corner, Millwall. Conceded by David Stockdale.
Attempt saved. Jake Cooper (Millwall) header from the left side of the six yard box is saved in the top left corner. Assisted by Shaun Williams with a cross.
Foul by Maikel Kieftenbeld (Birmingham City).