Spain will now meet the Netherlands in Sunday's final at Soccer City in Johannesburg. Germany will play for third place against Uruguay on Saturday in Port Elizabeth.
Veteran centre-back Puyol settled the semi-final encounter at the Durban Stadium with a forceful header in the 73rd minute, leaving German keeper Manuel Neuer no chance.
"We just didn't do enough up front and that allowed Spain to create chances," said Neuer. "We know we played a good tournament. But right now the disappointment is huge."
Germany coach Jogi Löw admitted his young side, which had dazzled with its great football during this summer's World Cup, had met its match.
"Compliments to Spain," he said. "I believe they will be the world champions, they have been the best for the last two or three years."
Spain coach Vicente Del Bosque had sprung a surprise before kick-off by dropping out-of-form Liverpool forward Fernando Torres and replacing him with Barcelona youngster Pedro.
That move allowed tournament top scorer David Villa to play through the middle and the switch almost paid dividends six minutes into the game.
Pedro came in off his wing and slipped a delightful ball in behind the Germany defence which Villa ran on to but he had to slide in to shoot before Neuer arrived and he succeeded only in prodding the ball into the goalkeeper's chest.
Spain should have gone ahead on 14 minutes after a cleverly-worked corner routine left Andres Iniesta free to cross from the right, but Puyol got too much purchase on his diving header and sent the ball over the bar.
Germany were stretched again on 19 minutes as a great crossfield ball from Xabi Alonso found the marauding Sergio Ramos but after an exquisite piece of control his shot was wild and high.
"We weren't courageous enough up front in the first half," said a visibly upset Germany captain Philipp Lahm right after the match. "But you have to say the Spaniards are a really strong team."
Germany finally threatened on 32 minutes when Piotr Trochowski, in for the suspended Thomas Müller, shot from distance and forced Iker Casillas into a
sprawling save low to his left.
Mesut Özil thought he should have had a penalty on the stroke of half-time when he went down under a challenge from Ramos but his appeals were waved away
by referee Viktor Kassai.
For all their pretty approach play, Spain were struggling to get into the box and started hitting pot shots from distance with Alonso, twice, and Villa shooting wide.
Neuer then saved from Pedro, who moments later shot wide while Villa was inches away from a tap-in after Iniesta jinked into the box and squared the ball across goal.
Up the other end substitute Marcell Jansen's cross was flicked on by a Spanish head, but on the turn Miroslav Klose could only volley high. And on 69 minutes Germany created possibly their best chance of the match as substitute Toni Kroos arrived unmarked at the back post onto Lukas Podolski's cross but his finish was weak and straight at Casillas.
But then came Puyol's great leap to propel Spain into the final. Spain had several chances on the counter attack late on but Pedro wasted the best of those as he tried to turn Arne Friedrich once too often instead of squaring to the unmarked substitute Torres.