Pep Guardiola's side started with their usual intensity, and it took them less than a minute to create their first chance. Jeremy Doku looked certain to score inside 30 seconds, but Villarreal goalkeeper Luiz Junior produced a superb one-handed save to deny him.
City, however, were relentless, and their pressure paid off in the 17th minute when Erling Haaland headed home from Rico Lewis's low cross, extending his remarkable scoring streak to 12 consecutive games for club and country.
Villarreal, who sit third in La Liga after a strong start to the season, struggled to recover from City's early dominance. Their only notable effort in the first half came when Pape Gueye fired narrowly wide from the edge of the box.
City then doubled their lead before the break, with Savinho, making a big impression on his European debut, delivering a fine cross for Bernardo Silva to head into the corner unmarked.
The second half saw City in full control, dictating the tempo and frustrating the hosts. Savinho continued to shine, forcing another save from Luiz Junior on the hour mark. Villarreal did manage a brief spell of pressure around the 70th minute, when Gueye tested Ederson and Nicolas Pepe headed just wide moments later, but the Spanish side never looked like mounting a serious comeback.
Haaland could have added to his tally late on, denied twice by Luiz Júnior, but City were rarely troubled as they saw out a comfortable win. The result marked only their fifth away victory in their last 15 Champions League trips to Spain and moved them to fifth in the league phase standings with seven points from three games.
Guardiola praised his side's control and resilience after the match, saying, "We have the feeling that again we can control the games."
Villarreal, meanwhile, remain winless in the competition after three games (one draw, two losses), leaving their hopes of progressing hanging by a thread.