조명연합군은 평양성 서쪽 외성에서 공격을 시작하여 모란봉, 칠성문, 보통문을 공격하고 이일과 김응서는 함구문을 공격하기로 했다. 명나라의 부총병 오유충과 조선의 승병 부대가 처음으로 공격을 시작해 거짓으로 패한척 후퇴하다가 다시 반격하여 승리했다. 하지만 조선군 8,000명이 남쪽 함구문에서 일본군의 매복에 걸려 상당한 손실을 입었다.
2월 7일(음력 1월 7일) 새벽 일본군 3천명이 명나라의 양호, 이여백, 장세작 등의 진지에 기습 공격을 가했으나 명나라군의 이를 물리쳤고 조명연합군은 본진을 보통문 앞에 전진 배치하고 정희현과 김경서의 기병대가 일본군을 유인하게 했으나 일본군은 속지 않았다.
이후 조명연합군은 2일 동안 탐색전을 하여 일본군을 관찰하다가 2월 8일(음력 1월 8일) 대공세를 가했다. 명나라군의 대장군포, 위원포, 자모포, 연주포, 불랑기포 등 수많은 대포들을 평양성에 집중사격하고 외성 서남쪽 함구문은 명군의 조승훈과 조선의 이일, 김경서의 8천명 군사가, 칠성문은 장세작이, 보통문은 양호가, 모란봉은 오유충과 사명대사의 승병 2,200명이 공격에 나섰다. 양측의 격렬한 전투가 벌어졌고 오유충은 적의 탄환에 맞았음에도 불구하고 군사들을 독려하고 이여송도 타고 있던 말이 적의 탄환에 맞아 죽자 다른 말로 갈아타 군사를 지휘해 사기를 올렸다.
조명연합군이 외성과 읍성을 함락시키고 중성으로 돌입해 일본군을 만수대와 을밀대로 압박했고 일본군은 풍월정 아래에 굴을 파 최후의 공격을 가했다. 격렬한 전투로 양측의 사상자가 늘어나자 이여송은 더 이상 공격하지 않고 철수했다. 그리고 고니시 유키나가와 협상을 맺어 추격하지 않는다는 약속을 한 뒤 이날 밤 평양성에서 철수했다.
그러나 명군의 참장 이녕의 3천명 군사가 추격에 나서 358명을 사살했고 조선군도 추격에 나서 황해도 방어사 이시언이 60명을, 황주 판관 정화가 120명 등 총 538명을 사살했다. 이리하여 평양성은 7개월만인 2월 9일(음력 1월 9일) 조명 연합군에 의해 탈환되었다.
평양성에서 후퇴하던 일본군은 봉산 - 용천(서흥군) - 배천을 거쳐 한양으로 철수했으나 그 과정에서 18,700명에 달하는 병력이 6,600명으로 감소될 정도로 피해가 컸다.
그러나 이후 평양성 탈환에 가장 큰 역할을 한 명나라군의 남병들이 이여송이 약속한 5,000냥의 은을 주지 않았다는 이유로 반란을 일으켰다. 이여송은 후방 의주에 남병 1,300명을 유인해 죽이라는 명령을 내렸다. 한편 황해도 방어사 이시언은 공을 세우지 못하자 자기 휘하의 군사들 중 허약한 군사들 60명을 모두 죽였다. 그리고 선조는 평양성이 탈환되자 2월 18일(음력 1월 18일) 의주를 떠나 남하를 시작했다.