Faits : A. Tous deux membres du Parti ouvrier et populaire vaudois (POP), B.A._ et A.B._ avaient tissé des liens d'amitié très étroits. Par contrat du 18 juin 2001, B.A._ a remis à bail à A.B._ un appartement de trois pièces et hall, au 4 ème étage de l'immeuble sis ... , à Lausanne. Conclu pour une durée initiale de cinq ans avec échéance au 30 septembre 2006, le bail était renouvelable tacitement de cinq ans en cinq ans, sauf avis de résiliation reçu au moins quatre mois à l'avance. Fixé tout d'abord à 1'620 fr. charges comprises, le loyer mensuel a passé dès le 1 er juillet 2009 à 1'624 fr., plus 125 fr. de charges. A.B._ occupe l'appartement avec son épouse, B.B._ et leur jeune fils. B.A._ est décédé en 2007. Son fils, C.A._, est alors devenu nu-propriétaire de l'immeuble et sa veuve, A.A._, également amie de longue date de A.B._, en est actuellement usufruitière (ci-après: la bailleresse). L'immeuble de l'avenue xxx présente un caractère familial. B.A._ et A.A._ y vivaient et celle-ci y demeure encore. C.A._ et son épouse, ainsi que la soeur de A.A._, logent également à cette adresse. C.A._ s'occupe de l'immeuble en collaboration avec une gérance. Après le décès de B.A._, les rapports entre A.B._ et C.A._ se sont peu à peu dégradés. En avril 2011, le locataire a déposé une plainte pénale contre le fils de la bailleresse, l'accusant d'avoir saccagé son jardin sans l'avertir et ainsi détruit cultures et plantations. La gérance a alors fait observer à A.B._ que l'usage d'un jardin lui avait été octroyé à bien plaire, qu'aucune plantation n'avait été détruite et qu'il avait refusé l'autre emplacement proposé par le propriétaire. Par ordonnance du 8 février 2012, le Ministère public de l'arrondissement de Lausanne a ordonné le classement de la procédure pénale; il a constaté qu'aucune plantation n'avait été détruite et a considéré que C.A._ n'avait causé aucun dommage à la propriété en enlevant la paille déposée sur le sol par le locataire et en mettant de côté les tuteurs plantés sur la parcelle. Par courrier du 13 juillet 2012, A.A._ a notifié à A.B._ une hausse de loyer de 178 fr. par mois, motivée par l'évolution de l'indice suisse des prix à la consommation ainsi que par la réfection de l'étanchéité de la toiture et des terrasses. Le locataire a contesté cette augmentation. Les parties ne sont pas parvenues à un accord devant la Commission de conciliation. En date du 6 décembre 2012, A.B._ a ouvert action devant le Tribunal des baux du canton de Vaud. Parallèlement à cette procédure, le locataire a adressé plusieurs courriels au POP. Le 1 er novembre 2012, il écrit ce qui suit au président de la section de Lausanne: " Je voulais te soumettre un problème. Merci d'en discuter au comité de section de Lausanne. Dans mon immeuble où je suis locataire, nous sommes en lutte contre le propriétaire, qui veut augmenter les loyers de 11%. Or, le propriétaire est membre du POP à ma connaissance...en tout cas il y a 2 ans encore. Il s'appelle C.A._. Je veux savoir s'il est encore membre de notre parti. Car si c'est le cas, je pense que c'est scandaleux et je vais le rendre public. " Après avoir été relancé par A.B._, le président de la section de Lausanne a confirmé que C.A._ était bien membre du POP. Le 20 novembre 2012, A.B._ a adressé au comité de section du POP & Gauche en mouvement section de Lausanne (GM) le courriel suivant: " (...) Depuis plusieurs mois, les locataires de mon immeuble sont en lutte contre les augmentations de loyer du propriétaire. Pour ma part, j'ai reçu une hausse de 11% pour fin septembre 2012, que je conteste en ce moment devant le tribunal des baux, avec l'aide de l'ASLOCA qui veut en faire un combat modèle jusqu'au TF. Il se trouve que le propriétaire est membre de notre parti. La position de notre parti sur les questions de logement me paraît claire. (...) Ma demande est simple: peut-on être membre du parti et avoir une pratique de propriétaire scandaleuse envers ses locataires? Comme cette affaire risque bien un jour de venir sur la place publique, car dans la procédure qui suivra son cours, le POP sera concerné... Il n'est en effet pas anodin qu'un locataire popiste soit en lutte contre son proprio popiste. Et avant que cela fasse les choux gras d'une certaine presse, voire la nôtre, je propose que le POP prenne les devants. Je demande au comité de section: 1. De prendre position sur des pratiques scandaleuses d'un propriétaire popiste qui augmente ses loyers de manière abusive. 2. De jouer un rôle de médiation avant que cela sorte sur la place publique. 3. Puis de prendre des mesures disciplinaires en cas de besoin: radiation, exclusion ou demande de retrait du membre pour des questions de cohérence politique (voir article 24 de nos statuts). Quoi qu'il en soit, je demande que le comité de section prenne position. " Ne recevant pas de réponse à ce courriel, A.B._ a fait savoir, le 3 décembre 2012, qu'il l'envoyait "à quelques élus communaux du POP & GM" en précisant qu'il s'agissait, dans un premier temps, d'"élargir le cercle de réflexion". Le même jour, il lui a été répondu qu'étant donné qu'il évoquait une mesure aussi sérieuse que l'exclusion - exceptionnelle selon l'art. 24 des statuts du parti - sa demande devait d'abord être examinée par le comité de section, lequel privilégiait une médiation avant une éventuelle procédure d'exclusion et souhaitait le rencontrer ainsi que consulter le dossier de l'ASLOCA. Le 21 janvier 2013, A.B._ a répondu au comité en ces termes: " Je serais heureux de prendre connaissance des démarches que vous entendez entreprendre. Et je vous prie de prendre la chose au sérieux, car il peut y avoir une expulsion au programme, car le proprio n'est pas tendre, malgré sa carte du parti. Donc, pour l'heure, je souhaite juste que vous m'indiquiez ce que la section de Lausanne, son comité me propose de faire. Ensuite, je vous dis mon avis. " Sans réponse au 3 février 2013, A.B._ a interpellé à nouveau le comité: " Je vois que certains ont décidé de faire les morts. Alors mes questions sont simples: 1. Qui est membre du comité de section pour que j'envois (sic) cette lettre à chacun/e. 2. Qui sont les élus du POP & GM (tous!) pour qu'ils reçoivent cette missive. Merci de la réponse rapide de la direction de section. Par ailleurs, si d'ici le 11 février, je n'ai pas de réponse officielle de la section de Lausanne (la mienne) sur ma demande non pas de décision, mais ma demande de démarche que la section peut entreprendre dans ce cas précis, je ferais (sic) valoir mes droits au POP & GM cantonal selon les statuts, puis je continuerais (sic) plus loin au cas où...car je n'ai pas l'habitude de laisser tomber mon os. Pour l'heure, j'imagine que vous avez remarqué que je n'ai pas voulu donner beaucoup de publicité à cette affaire, pour laisser du champ à une médiation possible...mais je suis en train de me demander si je n'ai pas eu tort. (...) PS 1: je vous ai envoyé le dossier de l'ASLOCA. (...) " Déclarant avoir compris ce dont son parti était capable, A.B._ a renoncé, le 12 février 2013, à poursuivre ses démarches auprès du POP et a refusé la rencontre qui lui était proposée. Il démissionnera du parti en date du 16 avril 2013. La bailleresse et son fils ont eu connaissance des courriels envoyés par A.B._ à différentes instances du POP. Par lettre du 15 avril 2013, A.A._ a renoncé à la hausse de loyer litigieuse et, par formule officielle du même jour adressée uniquement à A.B._, elle a résilié le bail avec effet au 31 mai 2013, subsidiairement au 31 juillet 2013; principalement, le congé était motivé par le manque d'égards envers l'usufruitière et le nu-propriétaire (art. 257f CO) et, subsidiairement, il était donné pour justes motifs (art. 266g CO). Le même jour, C.A._ a déposé une plainte pénale contre A.B._ pour dénonciation calomnieuse, en raison de la plainte pénale non fondée d'avril 2011 et des courriels envoyés au POP. L'ordonnance de non-entrée en matière a été confirmée le 12 juin 2013 par la Chambre des recours pénale du Tribunal cantonal du canton de Vaud. Le 21 juin 2013, A.B._ a écrit à A.A._; il lui proposait notamment de "se concentrer sur l'essentiel: vivre en toute harmonie dans l'immeuble". Le 3 juillet 2013, la bailleresse a répondu que, pour vivre en bonne harmonie dans l'immeuble, ce n'était pas seulement à elle qu'il fallait écrire, mais surtout à son fils C.A._ qui, chargé de l'entretien de l'immeuble, avait des contacts réguliers avec les locataires; elle ajoutait que son défunt mari n'aurait pas apprécié le dépôt de plainte d'avril 2011, ni les démarches entreprises auprès du POP. Le premier congé n'ayant pas été adressé à B.B._, une nouvelle résiliation a été notifiée le 30 août 2013 à chacun des époux locataires pour le 31 octobre 2013, subsidiairement pour le 30 juin 2014. Le courrier accompagnant la formule officielle mentionnait comme motifs du congé la plainte pénale déposée contre C.A._ pour "destruction de jardin" ainsi que les courriels envoyés aux instances dirigeantes du POP dénonçant le comportement prétendument scandaleux de C.A._; il était précisé que l'attitude du locataire avait profondément blessé la bailleresse et son fils et que, comme ceux-ci habitaient le même immeuble, la continuation du bail leur était intolérable. B. A.B._ et B.B._ ont contesté le congé. La tentative de conciliation a échoué. Par demande du 6 décembre 2013, les locataires ont conclu principalement à ce que le Tribunal des baux du canton de Vaud annule la résiliation notifiée le 30 août 2013; à titre subsidiaire, ils demandaient une prolongation du bail de quatre ans. Par la suite, ils ont encore conclu subsidiairement au versement d'une indemnité de 50'000 fr. pour le cas où la résiliation serait validée sur la base de l'art. 266g CO. Par jugement du 19 mai 2014, le Tribunal des baux a déclaré valable la résiliation du bail, avec effet au 30 juin 2014, accordé une seule et unique prolongation au 30 juin 2015, ordonné aux locataires de quitter les locaux loués au 30 juin 2015 au plus tard et dit qu'à défaut d'une exécution volontaire, il serait procédé à l'exécution forcée de la décision. Le tribunal a rejeté toutes autres ou plus amples conclusions, en particulier la conclusion subsidiaire des demandeurs tendant au paiement d'un montant de 50'000 fr. fondé sur l'art. 266g al. 2 CO. En substance, il a jugé tout d'abord que le congé anticipé litigieux ne remplissait pas les conditions posées par l'art. 257f al. 3 CO, notamment parce que les comportements reprochés au locataire n'étaient pas en relation directe avec l'objet loué et ne pouvaient ainsi être qualifiés de manques d'égards envers les voisins, quand bien même la bailleresse et son fils habitaient l'immeuble. En revanche, le tribunal a considéré que l'attitude du locataire, révélée par les courriels adressés aux instances dirigeantes du POP lausannois, constituait un juste motif de résiliation du bail au sens de l'art. 266g al. 1 CO. Les locataires ont interjeté appel. Par arrêt du 11 novembre 2014, la Cour d'appel civile du Tribunal cantonal du canton de Vaud a admis l'appel et, statuant à nouveau, a dit que la résiliation était nulle. Contrairement aux premiers juges, la cour cantonale a jugé que le comportement reproché au locataire, critiquable du point de vue des convenances, devait être apprécié dans le cadre du litige divisant les parties à propos de la hausse de loyer. En elles-mêmes, les démarches entreprises auprès du POP n'avaient ainsi pas rendu l'exécution du contrat intolérable au sens de l'art. 266g al. 1 CO, ce qui était confirmé par le fait que la bailleresse avait attendu plusieurs semaines pour résilier le bail après avoir pris connaissance des courriels du locataire. C. A.A._ exerce un recours en matière civile. Elle demande principalement au Tribunal fédéral de réformer l'arrêt attaqué en ce sens que la résiliation notifiée le 30 août 2013 est valable et qu'une seule et unique prolongation au 30 juin 2015 est accordée aux locataires; elle prend en outre des conclusions tendant à l'exécution de l'arrêt à rendre. A titre subsidiaire, la recourante conclut à l'annulation de la décision entreprise et au renvoi de la cause à l'autorité cantonale. A.B._ et B.B._ proposent le rejet du recours. Par la suite, des observations ont été déposées de part et d'autre par les parties. Invitée à se prononcer, la cour cantonale se réfère aux considérants de son arrêt.