Une armée romaine de 50 000 hommes, sous le commandement de Quintus Petillius Cerialis, remonta le Rhin, chassant les rebelles germaniques de la région. Parvenus à Castra Vetera[3],[4]. (l'actuelle Xanten), les Romains établirent leur campement près d'une forteresse construite par le chef rebelle Caius Julius Civilis. Cette forteresse germanique s'élevait sur les ruines d'un camp romain, à l'endroit même où les Germains avaient massacré 4 000 romains un mois auparavant. Le lendemain matin, les Romains abandonnèrent leur camp et se dirigèrent vers une colline voisine où ils comptaient affronter les Bataves. De l'autre côté des marais, les forces de Civilis, composées de Bataves, de Cananefates, de Gurgernes, de Frisons, de Trévires, de Tenctères et de Bructères, étaient positionnées. Cerialis disposa ses troupes sur deux lignes : l'infanterie auxiliaire en première ligne, la cavalerie sur les ailes et les légions en soutien. Il laissa plusieurs cohortes en périphérie.
La bataille commença par un déluge de projectiles et de frondeurs germaniques, qui ne causa que peu de dégâts aux Romains. Puis, le gros des forces germaniques, armé de lances de 4 m de long, lança une attaque, tentant de percer les lignes romaines. Simultanément, la cavalerie des Bructères attaqua, traversant le Rhin sur un barrage construit à l'avance et frappant les auxiliaires romains. Deux légions, dépêchées en toute hâte par Cérialis, arrivèrent bientôt en renfort. Après une bataille acharnée, elles mirent en déroute l'ennemi, contraignant la majeure partie de ses forces à battre en retraite. Après plusieurs heures de combats, aucun des deux camps ne parvint à l'emporter. C'est alors que, grâce aux informations d'un déserteur batave, Cérialis repéra un point de passage stratégique vers l'arrière des lignes ennemies. Il détacha ensuite 240 Singulares (auxiliaires germaniques sous les ordres de Gaius Mincius) qui, traversant les marais à l'arrière des Gurgernes, les attaquèrent sur leur flanc droit. L'attaque fut un succès et la formation germanique fut brisée. À ce moment, le gros des troupes romaines de la seconde ligne lança un assaut frontal. Sous la pression des légionnaires, les Germains en déroute se replièrent vers le fleuve. Profitant de l'averse imminente et de l'obscurité grandissante, la plupart des survivants abandonnèrent le champ de bataille et traversèrent le Rhin. Cerialis ne les poursuivit pas et ordonna à ses hommes de regagner leur camp. À la suite de la bataille de Castra Vetera, les forces rebelles principales furent considérablement affaiblies. Les troupes de Cerialis, sans être inquiétées, se retirèrent sans encombre à Mayence.
La bataille se termine par la défaite des Romains de Quintus Petillius Cerialis[5] et la destruction de la forteresse légionnaire de Castra Vetera. Les deux vexillations laissées pour garder le castrum (de la Legio V Alaudae et de la Legio XV Primigenia), furent détruites[6],[7].
À la suite de la bataille , de nombreuses troupes romaines stationnées le long du Rhin et en Gaule ont fait défection pour rejoindre la révolte.